<!DOCTYPE html>


  <html class="dark page-post">


<head><meta name="generator" content="Hexo 3.9.0">
  <meta charset="utf-8">
  
  <title>Dva实践总结 | Poetry&#39;s Blog</title>

  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">

  
    <meta name="keywords" content="Dva,React,">
  

  <meta name="description" content="一、环境搭建$ npm install dva-cli -g# 创建应用$ dva new dva-quickstart# 启动$ npm start  react项目的推荐目录结构（如果使用dva脚手架创建，则自动生成如下）  |── /mock/             # 数据mock的接口文件  |── /src/              # 项目源码目录（我们开发的主要工作区域）">
<meta name="keywords" content="Dva,React">
<meta property="og:type" content="article">
<meta property="og:title" content="Dva实践总结">
<meta property="og:url" content="http://blog.poetries.top/2018/09/05/dva/index.html">
<meta property="og:site_name" content="Poetry&#39;s Blog">
<meta property="og:description" content="一、环境搭建$ npm install dva-cli -g# 创建应用$ dva new dva-quickstart# 启动$ npm start  react项目的推荐目录结构（如果使用dva脚手架创建，则自动生成如下）  |── /mock/             # 数据mock的接口文件  |── /src/              # 项目源码目录（我们开发的主要工作区域）">
<meta property="og:locale" content="zh-Hans">
<meta property="og:image" content="https://poetries1.gitee.io/img-repo/20191001/43.png">
<meta property="og:image" content="https://poetries1.gitee.io/img-repo/20191001/44.png">
<meta property="og:image" content="https://upload-images.jianshu.io/upload_images/1505342-9ecb8ba2d8fd3ec2.png">
<meta property="og:image" content="https://poetries1.gitee.io/img-repo/20191001/45.png">
<meta property="og:image" content="https://poetries1.gitee.io/img-repo/20191001/46.png">
<meta property="og:image" content="https://poetries1.gitee.io/img-repo/20191001/47.png">
<meta property="og:image" content="https://upload-images.jianshu.io/upload_images/6693922-948c3efcfeeaf4fd.gif">
<meta property="og:updated_time" content="2020-08-15T04:25:31.906Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Dva实践总结">
<meta name="twitter:description" content="一、环境搭建$ npm install dva-cli -g# 创建应用$ dva new dva-quickstart# 启动$ npm start  react项目的推荐目录结构（如果使用dva脚手架创建，则自动生成如下）  |── /mock/             # 数据mock的接口文件  |── /src/              # 项目源码目录（我们开发的主要工作区域）">
<meta name="twitter:image" content="https://poetries1.gitee.io/img-repo/20191001/43.png">

  

  
    <link rel="icon" href="/favicon.ico">
  

  <link href="/css/styles.css?v=c114cbeddx" rel="stylesheet">
<link href="/css/other.css?v=c114cbeddx" rel="stylesheet">


  
    <link rel="stylesheet" href="/css/personal-style.css">
  

  

  
  <script type="text/javascript">
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "//hm.baidu.com/hm.js?40b1f89aa80f2527b3db779c6898c879";
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(hm, s);
    })();
  </script>


  
  <script type="text/javascript">
	(function(){
	    var bp = document.createElement('script');
	    var curProtocol = window.location.protocol.split(':')[0];
	    if (curProtocol === 'https') {
	        bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';        
	    }
	    else {
	        bp.src = 'http://push.zhanzhang.baidu.com/push.js';
	    }
	    var s = document.getElementsByTagName("script")[0];
	    s.parentNode.insertBefore(bp, s);
	})();
  </script>



  
    <script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
    <link rel="stylesheet" href="//cdn.bootcss.com/font-awesome/4.3.0/css/font-awesome.min.css">
  

  <!-- 聊天系统 -->
  
    
   <link type="text/css" rel="stylesheet" href="/renxi/default.css">
   <style>
      #modal {
        position: static !important;
      }
      .filter {
        width: 100%;
        height: 100%;
        position: absolute;
        top: 0;
        left: 0;
        background: #fe5757;
        animation: colorChange 30s ease-in-out infinite;
        animation-fill-mode: both;
        mix-blend-mode: overlay;
      }
  
      @keyframes colorChange {
        0%, 100% {
            opacity: 0;
        }
        50% {
            opacity: .9;
        }
      }
   </style>
</head>
</html>
<body>
  
  
    <span id="toolbox-mobile" class="toolbox-mobile">导航</span>
  

  <div class="post-header CENTER">
   
  <div class="toolbox">
    <a class="toolbox-entry" href="/">
      <span class="toolbox-entry-text">导航</span>
      <i class="icon-angle-down"></i>
      <i class="icon-home"></i>
    </a>
    <ul class="list-toolbox">
      
        <li class="item-toolbox">
          <a
            class="CIRCLE"
            href="/archives/"
            rel="noopener noreferrer"
            target="_self"
            >
            博客
          </a>
        </li>
      
        <li class="item-toolbox">
          <a
            class="CIRCLE"
            href="/categories/"
            rel="noopener noreferrer"
            target="_self"
            >
            分类
          </a>
        </li>
      
        <li class="item-toolbox">
          <a
            class="CIRCLE"
            href="/tags/"
            rel="noopener noreferrer"
            target="_self"
            >
            标签
          </a>
        </li>
      
        <li class="item-toolbox">
          <a
            class="CIRCLE"
            href="/search/"
            rel="noopener noreferrer"
            target="_self"
            >
            搜索
          </a>
        </li>
      
        <li class="item-toolbox">
          <a
            class="CIRCLE"
            href="/link/"
            rel="noopener noreferrer"
            target="_self"
            >
            友链
          </a>
        </li>
      
        <li class="item-toolbox">
          <a
            class="CIRCLE"
            href="/about/"
            rel="noopener noreferrer"
            target="_self"
            >
            关于
          </a>
        </li>
      
    </ul>
  </div>


</div>


  <div id="toc" class="toc-article">
    <strong class="toc-title">文章目录<i class="iconfont toc-title" style="display:inline-block;color:#87998d;width:20px;height:20px;">&#xf004b;</i></strong>
    <ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#一、环境搭建"><span class="toc-text">一、环境搭建</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#二、初识Dva"><span class="toc-text">二、初识Dva</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#2-1-Dva的特性"><span class="toc-text">2.1 Dva的特性</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-2-Dva的五个API"><span class="toc-text">2.2 Dva的五个API</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#2-2-1-app-dva-Opts"><span class="toc-text">2.2.1 app = dva(Opts)</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-2-2-app-use-Hooks"><span class="toc-text">2.2.2 app.use(Hooks)</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-2-3-app-model-ModelObject"><span class="toc-text">2.2.3 app.model(ModelObject)</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-2-4-app-unmodel-namespace"><span class="toc-text">2.2.4 app.unmodel(namespace)</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-2-5-app-router-Function"><span class="toc-text">2.2.5 app.router(Function)</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-2-6-app-start"><span class="toc-text">2.2.6 app.start</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-3-Dva九个概念"><span class="toc-text">2.3 Dva九个概念</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#2-3-1-State"><span class="toc-text">2.3.1 State</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-3-2-Action"><span class="toc-text">2.3.2 Action</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-3-3-Model"><span class="toc-text">2.3.3 Model</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-3-4-Router"><span class="toc-text">2.3.4 Router</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-4-整体架构"><span class="toc-text">2.4 整体架构</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-5-Dva图解"><span class="toc-text">2.5 Dva图解</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#三、计数器例子"><span class="toc-text">三、计数器例子</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#四、Dva实践"><span class="toc-text">四、Dva实践</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#4-1-抽离Model"><span class="toc-text">4.1 抽离Model</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#4-2-设计组件"><span class="toc-text">4.2 设计组件</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#4-2-1-容器组件"><span class="toc-text">4.2.1 容器组件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-2-2-展示组件"><span class="toc-text">4.2.2 展示组件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-2-3-设置路由"><span class="toc-text">4.2.3 设置路由</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-2-4-设计容器组件"><span class="toc-text">4.2.4 设计容器组件</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#4-3-添加Reducer"><span class="toc-text">4.3 添加Reducer</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#4-3-1-第一步：实现reducer函数"><span class="toc-text">4.3.1 第一步：实现reducer函数</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-3-2-第二步：关联Model中的数据源"><span class="toc-text">4.3.2 第二步：关联Model中的数据源</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-3-3-第三步：通过发起Action，在组件中获取Model中的数据"><span class="toc-text">4.3.3 第三步：通过发起Action，在组件中获取Model中的数据</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-3-4-第四步：-在index-js中添加models"><span class="toc-text">4.3.4 第四步： 在index.js中添加models</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-4-添加Effects"><span class="toc-text">4.4 添加Effects</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-5-把请求处理分离到service中"><span class="toc-text">4.5 把请求处理分离到service中</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#五、使用dva框架和直接使用redux写法的区别"><span class="toc-text">五、使用dva框架和直接使用redux写法的区别</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#5-1-使用-redux"><span class="toc-text">5.1 使用 redux</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#5-2-使用dva"><span class="toc-text">5.2 使用dva</span></a></li></ol></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#六、使用axios统一处理"><span class="toc-text">六、使用axios统一处理</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#6-1-示例代码"><span class="toc-text">6.1 示例代码</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#6-2-明确响应体"><span class="toc-text">6.2 明确响应体</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#6-3-依赖包分析"><span class="toc-text">6.3 依赖包分析</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#6-4-axios-全局配置"><span class="toc-text">6.4 axios 全局配置</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#6-5-加载-NProgress-过渡组件"><span class="toc-text">6.5 加载 NProgress 过渡组件</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#6-6-网络请求成功处理"><span class="toc-text">6.6 网络请求成功处理</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#6-7-网络请求失败处理"><span class="toc-text">6.7 网络请求失败处理</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#七、更多参考"><span class="toc-text">七、更多参考</span></a></li></ol>
  </div>
  




<div class="content content-post CENTER">
   <!-- canvas 彩带 -->
<canvas id="evanyou" width="1302" height="678" style="position: fixed;width: 100%;height: 100%;top: 0;left:0;z-index:-1;"></canvas>

<article id="post-dva" class="article article-type-post" itemprop="blogPost">
  <header class="article-header" style="position:relative;">
    <h1 class="post-title">Dva实践总结</h1>

    <div class="article-meta">
      <span>
        <i class="icon-calendar"></i>
        <span>2018.09.05</span>
      </span>

      
        <span class="article-author">
          <i class="icon-user"></i>
          <span>Poetry</span>
        </span>
      

      
  <span class="article-category">
    <i class="icon-list"></i>
    <a class="article-category-link" href="/categories/Front-End/">Front-End</a>
  </span>



      

      
      <i class="fa fa-eye"></i> 
        <span id="busuanzi_container_page_pv">
           &nbsp热度 <span id="busuanzi_value_page_pv">
           <i class="fa fa-spinner fa-spin"></i></span>℃
        </span>
      
      
       
          <span class="post-count">
            <i class="fa fa-file-word-o"></i>&nbsp
            <span>字数统计 9.4k字</span>
          </span>

          <span class="post-count">
            <i class="fa fa-columns"></i>&nbsp
            <span>阅读时长 43分</span>
          </span>
      
      
    </div>

    <i class="iconfont" id="toc-eye" style="display:inline-block;color:#b36619;position:absolute;top:0;right:0;cursor:pointer;
    font-size: 24px;">&#xe61c;</i>

  </header>

  <div class="article-content">
    
      <div id="container">
        <h1 id="一、环境搭建"><a href="#一、环境搭建" class="headerlink" title="一、环境搭建"></a>一、环境搭建</h1><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> npm install dva-cli -g</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 创建应用</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> dva new dva-quickstart</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 启动</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> npm start</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>react项目的推荐目录结构（如果使用dva脚手架创建，则自动生成如下）</p>
</blockquote>
<figure class="highlight"><table><tr><td class="code"><pre><span class="line">|── /mock/             # 数据mock的接口文件  </span><br><span class="line">|── /src/              # 项目源码目录（我们开发的主要工作区域）   </span><br><span class="line">|   |── /components/   # 项目组件（用于路由组件内引用的可复用组件）   </span><br><span class="line">|   |── /routes/       # 路由组件（页面维度） </span><br><span class="line">|   |  |── route1.js  </span><br><span class="line">|   |  |── route2.js   # 根据router.js中的映射，在不同的url下，挂载不同的路由组件</span><br><span class="line">|   |  └── route3.js    </span><br><span class="line">|   |── /models/       # 数据模型（可以理解为store，用于存储数据与方法）  </span><br><span class="line">|   |  |── model1.js  </span><br><span class="line">|   |  |── model2.js   # 选择分离为多个model模型，是根据业务实体进行划分</span><br><span class="line">|   |  └── model3.js  </span><br><span class="line">|   |── /services/     # 数据接口（处理前台页面的ajax请求，转发到后台）   </span><br><span class="line">|   |── /utils/        # 工具函数（工具库，存储通用函数与配置参数）     </span><br><span class="line">|   |── router.js       # 路由配置（定义路由与对应的路由组件）  </span><br><span class="line">|   |── index.js       # 入口文件  </span><br><span class="line">|   |── index.less      </span><br><span class="line">|   └── index.html     </span><br><span class="line">|── package.json       # 项目信息  </span><br><span class="line">└── proxy.config.js    # 数据mock配置</span><br></pre></td></tr></table></figure>
<p><strong>使用 antd</strong></p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">npm i babel-plugin-import --save</span><br></pre></td></tr></table></figure>
<blockquote>
<p><code>babel-plugin-import</code> 是用来按需加载 <code>antd</code> 的脚本和样式的</p>
</blockquote>
<ul>
<li>编辑 <code>.webpackrc</code>，使 <code>babel-plugin-import</code> 插件生效</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">&#123;</span><br><span class="line">+  <span class="string">"extraBabelPlugins"</span>: [</span><br><span class="line">+    [<span class="string">"import"</span>, &#123; <span class="string">"libraryName"</span>: <span class="string">"antd"</span>, <span class="string">"libraryDirectory"</span>: <span class="string">"es"</span>, <span class="string">"style"</span>: <span class="string">"css"</span> &#125;]</span><br><span class="line">+  ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h1 id="二、初识Dva"><a href="#二、初识Dva" class="headerlink" title="二、初识Dva"></a>二、初识Dva</h1><h2 id="2-1-Dva的特性"><a href="#2-1-Dva的特性" class="headerlink" title="2.1 Dva的特性"></a>2.1 Dva的特性</h2><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">dva = React-Router + Redux + Redux-saga</span><br></pre></td></tr></table></figure>
<ul>
<li>仅有 5 个<code>API</code>，仅有5个主要的<code>api</code></li>
<li>支持 <code>HMR</code>，支持模块的热更新</li>
<li>支持 <code>SSR (ServerSideRender)</code>，支持服务器端渲染</li>
<li>支持 <code>Mobile/ReactNative</code>，支持移动手机端的代码编写</li>
<li>支持<code>TypeScript</code></li>
<li>支持路由和 <code>Model</code> 的动态加载</li>
</ul>
<h2 id="2-2-Dva的五个API"><a href="#2-2-Dva的五个API" class="headerlink" title="2.2 Dva的五个API"></a>2.2 Dva的五个API</h2><p> <img src="https://poetries1.gitee.io/img-repo/20191001/43.png" alt></p>
<h3 id="2-2-1-app-dva-Opts"><a href="#2-2-1-app-dva-Opts" class="headerlink" title="2.2.1 app = dva(Opts)"></a>2.2.1 app = dva(Opts)</h3><blockquote>
<p><code>app = dva(Opts)</code>：创建应用，返回 <code>dva</code> 实例。(注：dva 支持多实例)**</p>
</blockquote>
<p>在<code>opts</code>可以配置所有的<code>hooks</code></p>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> app = dva(&#123;</span><br><span class="line">     history,</span><br><span class="line">     initialState,</span><br><span class="line">     onError,</span><br><span class="line">     onAction,</span><br><span class="line">     onStateChange,</span><br><span class="line">     onReducer,</span><br><span class="line">     onEffect,</span><br><span class="line">     onHmr,</span><br><span class="line">     extraReducers,</span><br><span class="line">     extraEnhancers,</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<blockquote>
<p>hooks包含如下配置项</p>
</blockquote>
<p>1、 <code>onError((err, dispatch) =&gt; {})</code></p>
<ul>
<li><code>effect</code> 执行错误或 <code>subscription</code> 通过<code>done</code> 主动抛错时触发，可用于管理全局出错状态</li>
<li>注意：<code>subscription</code> 并没有加 <code>try...catch</code>，所以有错误时需通过第二个参数 <code>done</code> 主动抛错</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">app.model(&#123;</span><br><span class="line">  subscriptions: &#123;</span><br><span class="line">    setup(&#123; dispatch &#125;, done) &#123;</span><br><span class="line">      done(e)</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>
<p>2、 <code>onAction(fn | fn[])</code></p>
<blockquote>
<p>在<code>action</code>被<code>dispatch</code>时触发，用于注册 <code>redux</code> 中间件。支持函数或函数数组格式</p>
</blockquote>
<ul>
<li>例如我们要通过 <code>redux-logger</code> 打印日志</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> createLogger <span class="keyword">from</span> <span class="string">'redux-logger'</span>;</span><br><span class="line"><span class="keyword">const</span> app = dva(&#123;</span><br><span class="line">  onAction: createLogger(opts),</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>
<p>3、 <code>onStateChange(fn)</code></p>
<blockquote>
<p><code>state</code> 改变时触发，可用于同步 state 到 <code>localStorage</code>，服务器端等</p>
</blockquote>
<p>4、 <code>onReducer(fn)</code></p>
<blockquote>
<p>封装 <code>reducer</code> 执行，比如借助 <code>redux-undo</code> 实现 <code>redo/undo</code></p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> undoable <span class="keyword">from</span> <span class="string">'redux-undo'</span>;</span><br><span class="line"><span class="keyword">const</span> app = dva(&#123;</span><br><span class="line">  onReducer: <span class="function"><span class="params">reducer</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="function">(<span class="params">state, action</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">const</span> undoOpts = &#123;&#125;;</span><br><span class="line">      <span class="keyword">const</span> newState = undoable(reducer, undoOpts)(state, action);</span><br><span class="line">      <span class="comment">// 由于 dva 同步了 routing 数据，所以需要把这部分还原</span></span><br><span class="line">      <span class="keyword">return</span> &#123; ...newState, <span class="attr">routing</span>: newState.present.routing &#125;;</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>
<p>5、 <code>onEffect(fn)</code></p>
<blockquote>
<p>封装 <code>effect</code> 执行。比如 <code>dva-loading</code> 基于此实现了自动处理 <code>loading</code> 状态</p>
</blockquote>
<p>6、 <code>onHmr(fn)</code></p>
<blockquote>
<p>热替换相关，目前用于 <code>babel-plugin-dva-hmr</code></p>
</blockquote>
<p>7、 <code>extraReducers</code></p>
<blockquote>
<p>指定额外的 <code>reducer</code>，比如 <code>redux-form</code> 需要指定额外的 <code>form reducer</code></p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; reducer <span class="keyword">as</span> formReducer &#125; <span class="keyword">from</span> <span class="string">'redux-form'</span></span><br><span class="line"><span class="keyword">const</span> app = dva(&#123;</span><br><span class="line">  extraReducers: &#123;</span><br><span class="line">    form: formReducer,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>
<blockquote>
<p>这里比较常用的是，<code>history</code>的配置，一般默认的是<code>hashHistory</code>，如果要配置 <code>history</code>为 <code>browserHistory</code>，可以这样</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> createHistory <span class="keyword">from</span> <span class="string">'history/createBrowserHistory'</span>;</span><br><span class="line"><span class="keyword">const</span> app = dva(&#123;</span><br><span class="line">  history: createHistory(),</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<blockquote>
<p><code>initialState</code>：指定初始数据，优先级高于 <code>model</code> 中的 <code>state</code>，默认是 <code>{}</code>，但是基本上都在<code>modal</code>里面设置相应的<code>state</code></p>
</blockquote>
<h3 id="2-2-2-app-use-Hooks"><a href="#2-2-2-app-use-Hooks" class="headerlink" title="2.2.2 app.use(Hooks)"></a>2.2.2 app.use(Hooks)</h3><blockquote>
<p>app.use(Hooks)：配置 hooks 或者注册插件</p>
</blockquote>
<p>这里最常见的就是<code>dva-loading</code>插件的配置</p>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> createLoading <span class="keyword">from</span> <span class="string">'dva-loading'</span>;</span><br><span class="line">...</span><br><span class="line">app.use(createLoading(opts));</span><br></pre></td></tr></table></figure>
<blockquote>
<p>但是一般对于全局的<code>loading</code>我们会根据业务的不同来显示相应不同的<code>loading</code>图标，我们可以根据自己的需要来选择注册相应的插件</p>
</blockquote>
<h3 id="2-2-3-app-model-ModelObject"><a href="#2-2-3-app-model-ModelObject" class="headerlink" title="2.2.3 app.model(ModelObject)"></a>2.2.3 app.model(ModelObject)</h3><blockquote>
<p><code>app.model(ModelObject)</code>：这个是你数据逻辑处理，数据流动的地方</p>
</blockquote>
<p> <img src="https://poetries1.gitee.io/img-repo/20191001/44.png" alt></p>
<h3 id="2-2-4-app-unmodel-namespace"><a href="#2-2-4-app-unmodel-namespace" class="headerlink" title="2.2.4 app.unmodel(namespace)"></a>2.2.4 app.unmodel(namespace)</h3><blockquote>
<p>取消 <code>model</code> 注册，清理 <code>reducers</code>,<code>effects</code> 和 <code>subscriptions</code>。<code>subscription</code> 如果没有返回 <code>unlisten</code> 函数，使用 <code>app.unmodel</code> 会给予警告</p>
</blockquote>
<h3 id="2-2-5-app-router-Function"><a href="#2-2-5-app-router-Function" class="headerlink" title="2.2.5 app.router(Function)"></a>2.2.5 app.router(Function)</h3><blockquote>
<p>注册路由表，这一操作步骤在dva中也很重要</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 注册路由</span></span><br><span class="line">app.router(<span class="built_in">require</span>(<span class="string">'./router'</span>))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 路由文件</span></span><br><span class="line"><span class="keyword">import</span> &#123; Router, Route &#125; <span class="keyword">from</span> <span class="string">'dva/router'</span>;</span><br><span class="line"><span class="keyword">import</span> IndexPage <span class="keyword">from</span> <span class="string">'./routes/IndexPage'</span></span><br><span class="line"><span class="keyword">import</span> TodoList <span class="keyword">from</span> <span class="string">'./routes/TodoList'</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">RouterConfig</span>(<span class="params">&#123; history &#125;</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    &lt;Router history=&#123;history&#125;&gt;</span><br><span class="line">        &lt;Route path=<span class="string">"/"</span> component=&#123;IndexPage&#125; /&gt;</span><br><span class="line">        &lt;Route path=<span class="string">'/todoList'</span> components=&#123;TodoList&#125;/&gt;</span><br><span class="line">    &lt;<span class="regexp">/Router&gt;</span></span><br><span class="line"><span class="regexp">  )</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br><span class="line"><span class="regexp">export default RouterConfig</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>如果我们想解决组件动态加载问题，我们的路由文件也可以按照下面的写法来写</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; Router, Switch, Route &#125; <span class="keyword">from</span> <span class="string">'dva/router'</span></span><br><span class="line"><span class="keyword">import</span> dynamic <span class="keyword">from</span> <span class="string">'dva/dynamic'</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">RouterConfig</span>(<span class="params">&#123; history, app &#125;</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> IndexPage = dynamic(&#123;</span><br><span class="line">    app,</span><br><span class="line">    component: <span class="function"><span class="params">()</span> =&gt;</span> <span class="keyword">import</span>(<span class="string">'./routes/IndexPage'</span>),</span><br><span class="line">  &#125;)</span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> Users = dynamic(&#123;</span><br><span class="line">    app,</span><br><span class="line">    models: <span class="function"><span class="params">()</span> =&gt;</span> [<span class="keyword">import</span>(<span class="string">'./models/users'</span>)],</span><br><span class="line">    component: <span class="function"><span class="params">()</span> =&gt;</span> <span class="keyword">import</span>(<span class="string">'./routes/Users'</span>),</span><br><span class="line">  &#125;)</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    &lt;Router history=&#123;history&#125;&gt;</span><br><span class="line">      &lt;Switch&gt;</span><br><span class="line">        &lt;Route exact path=<span class="string">"/"</span> component=&#123;IndexPage&#125; /&gt;</span><br><span class="line">        &lt;Route exact path=<span class="string">"/users"</span> component=&#123;Users&#125; /&gt;</span><br><span class="line">      &lt;<span class="regexp">/Switch&gt;</span></span><br><span class="line"><span class="regexp">    &lt;/</span>Router&gt;</span><br><span class="line">  )</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> RouterConfig</span><br></pre></td></tr></table></figure>
<blockquote>
<p>其中<code>dynamic(opts)</code> 中<code>opt</code>包含三个配置项：</p>
</blockquote>
<ul>
<li><code>app</code>: <code>dva</code> 实例，加载 <code>models</code> 时需要</li>
<li><code>models</code>: 返回 <code>Promise</code> 数组的函数，<code>Promise</code>返回 dva model`</li>
<li><code>component</code>：返回 <code>Promise</code>的函数，<code>Promise</code>返回 <code>React Component</code></li>
</ul>
<h3 id="2-2-6-app-start"><a href="#2-2-6-app-start" class="headerlink" title="2.2.6 app.start"></a>2.2.6 app.start</h3><blockquote>
<p>启动应用，即将我们的应用跑起来</p>
</blockquote>
<h2 id="2-3-Dva九个概念"><a href="#2-3-Dva九个概念" class="headerlink" title="2.3 Dva九个概念"></a>2.3 Dva九个概念</h2><h3 id="2-3-1-State"><a href="#2-3-1-State" class="headerlink" title="2.3.1 State"></a>2.3.1 State</h3><blockquote>
<p>初始值，我们在 <code>dva()</code> 初始化的时候和在 modal 里面的 <code>state</code> 对其两处进行定义，其中 modal 中的优先级低于传给  <code>dva()</code> 的  <code>opts.initialState</code></p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// dva()初始化</span></span><br><span class="line"><span class="keyword">const</span> app = dva(&#123;</span><br><span class="line">  initialState: &#123; <span class="attr">count</span>: <span class="number">1</span> &#125;,</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// modal()定义事件</span></span><br><span class="line">app.model(&#123;</span><br><span class="line">  namespace: <span class="string">'count'</span>,</span><br><span class="line">  state: <span class="number">0</span>,</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h3 id="2-3-2-Action"><a href="#2-3-2-Action" class="headerlink" title="2.3.2 Action"></a>2.3.2 Action</h3><blockquote>
<p>表示操作事件，可以是同步，也可以是异步</p>
</blockquote>
<ul>
<li><code>action</code> 的格式如下，它需要有一个 <code>type</code>，表示这个 <code>action</code> 要触发什么操作；<code>payload</code> 则表示这个 <code>action</code> 将要传递的数据</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  type: <span class="built_in">String</span>,</span><br><span class="line">  payload: data,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>我们通过 dispatch 方法来发送一个 action</p>
</blockquote>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">dispatch(&#123; type: &apos;todos/add&apos;, payload: &apos;Learn Dva&apos; &#125;);</span><br></pre></td></tr></table></figure>
<blockquote>
<p>其实我们可以构建一个Action 创建函数，如下</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">addTodo</span>(<span class="params">text</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    type: ADD_TODO,</span><br><span class="line">    text</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//我们直接dispatch(addTodo()),就发送了一个action。</span></span><br><span class="line">dispatch(addTodo())</span><br></pre></td></tr></table></figure>
<h3 id="2-3-3-Model"><a href="#2-3-3-Model" class="headerlink" title="2.3.3 Model"></a>2.3.3 Model</h3><blockquote>
<p><code>model</code> 是 <code>dva</code> 中最重要的概念，<code>Model</code> 非 <code>MVC</code> 中的 <code>M</code>，而是领域模型，用于把数据相关的逻辑聚合到一起，几乎所有的数据，逻辑都在这边进行处理分发</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> queryString <span class="keyword">from</span> <span class="string">'query-string'</span></span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> todoService <span class="keyword">from</span> <span class="string">'../services/todo'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span><br><span class="line">  namespace: <span class="string">'todo'</span>,</span><br><span class="line">  state: &#123;</span><br><span class="line">    list: []</span><br><span class="line">  &#125;,</span><br><span class="line">  reducers: &#123;</span><br><span class="line">    save(state, &#123; <span class="attr">payload</span>: &#123; list &#125; &#125;) &#123;</span><br><span class="line">      <span class="keyword">return</span> &#123; ...state, list &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;,</span><br><span class="line">  effects: &#123;</span><br><span class="line">    *addTodo(&#123; <span class="attr">payload</span>: value &#125;, &#123; call, put, select &#125;) &#123;</span><br><span class="line">      <span class="comment">// 模拟网络请求</span></span><br><span class="line">      <span class="keyword">const</span> data = <span class="keyword">yield</span> call(todoService.query, value)</span><br><span class="line">      <span class="built_in">console</span>.log(data)</span><br><span class="line">      <span class="keyword">let</span> tempList = <span class="keyword">yield</span> select(<span class="function"><span class="params">state</span> =&gt;</span> state.todo.list)</span><br><span class="line">      <span class="keyword">let</span> list = []</span><br><span class="line">      list = list.concat(tempList)</span><br><span class="line">      <span class="keyword">const</span> tempObj = &#123;&#125;</span><br><span class="line">      tempObj.title = value</span><br><span class="line">      tempObj.id = list.length</span><br><span class="line">      tempObj.finished = <span class="literal">false</span></span><br><span class="line">      list.push(tempObj)</span><br><span class="line">      <span class="keyword">yield</span> put(&#123; <span class="attr">type</span>: <span class="string">'save'</span>, <span class="attr">payload</span>: &#123; list &#125;&#125;)</span><br><span class="line">    &#125;,</span><br><span class="line">    *toggle(&#123; <span class="attr">payload</span>: index &#125;, &#123; call, put, select &#125;) &#123;</span><br><span class="line">      <span class="comment">// 模拟网络请求</span></span><br><span class="line">      <span class="keyword">const</span> data = <span class="keyword">yield</span> call(todoService.query, index)</span><br><span class="line">      <span class="keyword">let</span> tempList = <span class="keyword">yield</span> select(<span class="function"><span class="params">state</span> =&gt;</span> state.todo.list)</span><br><span class="line">      <span class="keyword">let</span> list = []</span><br><span class="line">      list = list.concat(tempList)</span><br><span class="line">      <span class="keyword">let</span> obj = list[index]</span><br><span class="line">      obj.finished = !obj.finished</span><br><span class="line">      <span class="keyword">yield</span> put(&#123; <span class="attr">type</span>: <span class="string">'save'</span>, <span class="attr">payload</span>: &#123; list &#125; &#125;)</span><br><span class="line">    &#125;,</span><br><span class="line">    *<span class="keyword">delete</span>(&#123; <span class="attr">payload</span>: index &#125;, &#123; call, put, select &#125;) &#123;</span><br><span class="line">      <span class="keyword">const</span> data = <span class="keyword">yield</span> call(todoService.query, index)</span><br><span class="line">      <span class="keyword">let</span> tempList = <span class="keyword">yield</span> select(<span class="function"><span class="params">state</span> =&gt;</span> state.todo.list)</span><br><span class="line">      <span class="keyword">let</span> list = []</span><br><span class="line">      list = list.concat(tempList)</span><br><span class="line">      list.splice(index, <span class="number">1</span>)</span><br><span class="line">      <span class="keyword">yield</span> put(&#123; <span class="attr">type</span>: <span class="string">'save'</span>, <span class="attr">payload</span>: &#123; list &#125; &#125;)</span><br><span class="line">    &#125;,</span><br><span class="line">    *modify(&#123; <span class="attr">payload</span>: &#123; value, index &#125; &#125;, &#123; call, put, select &#125;) &#123;</span><br><span class="line">      <span class="keyword">const</span> data = <span class="keyword">yield</span> call(todoService.query, value)</span><br><span class="line">      <span class="keyword">let</span> tempList = <span class="keyword">yield</span> select(<span class="function"><span class="params">state</span> =&gt;</span> state.todo.list)</span><br><span class="line">      <span class="keyword">let</span> list = []</span><br><span class="line">      list = list.concat(tempList)</span><br><span class="line">      <span class="keyword">let</span> obj = list[index]</span><br><span class="line">      obj.title = value</span><br><span class="line">      <span class="keyword">yield</span> put(&#123; <span class="attr">type</span>: <span class="string">'save'</span>, <span class="attr">payload</span>: &#123; list &#125; &#125;)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;,</span><br><span class="line">  subscriptions: &#123;</span><br><span class="line">    setup(&#123; dispatch, history &#125;) &#123;</span><br><span class="line">      <span class="comment">// 监听路由的变化，请求页面数据</span></span><br><span class="line">      <span class="keyword">return</span> history.listen(<span class="function">(<span class="params">&#123; pathname, search &#125;</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">const</span> query = queryString.parse(search)</span><br><span class="line">        <span class="keyword">let</span> list = []</span><br><span class="line">        <span class="keyword">if</span> (pathname === <span class="string">'todoList'</span>) &#123;</span><br><span class="line">          dispatch(&#123; <span class="attr">type</span>: <span class="string">'save'</span>, <span class="attr">payload</span>: &#123;list&#125; &#125;)</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p><code>model</code>对象中包含5个重要的属性</p>
</blockquote>
<p><strong>state</strong></p>
<blockquote>
<p>这里的 state 跟我们刚刚讲的 state 的概念是一样的，只不过她的优先级比初始化的低，但是基本上项目中的 state 都是在这里定义的</p>
</blockquote>
<p><strong>namespace</strong></p>
<blockquote>
<p><code>model</code> 的命名空间，同时也是他在全局 <code>state</code> 上的属性，只能用字符串，我们发送在发送 <code>action</code> 到相应的 <code>reducer</code> 时，就会需要用到 <code>namespace</code></p>
</blockquote>
<p><strong>Reducer</strong></p>
<blockquote>
<p>以<code>key/value</code> 格式定义 <code>reducer</code>，用于处理同步操作，唯一可以修改  <code>state</code> 的地方。由  <code>action</code> 触发。其实一个纯函数</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">namespace: <span class="string">'todo'</span>,</span><br><span class="line">  state: &#123;</span><br><span class="line">    list: []</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="comment">// reducers 写法</span></span><br><span class="line">  reducers: &#123;</span><br><span class="line">    save(state, &#123; <span class="attr">payload</span>: &#123; list &#125; &#125;) &#123;</span><br><span class="line">      <span class="keyword">return</span> &#123; ...state, list &#125;</span><br><span class="line">    &#125;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>
<p><strong>Effect</strong></p>
<blockquote>
<p>用于处理异步操作和业务逻辑，不直接修改 <code>state</code>，简单的来说，就是获取从服务端获取数据，并且发起一个 <code>action</code>交给<code>reducer</code> 的地方</p>
</blockquote>
<p>其中它用到了<code>redux-saga</code>，里面有几个常用的函数。</p>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// effects 写法</span></span><br><span class="line">effects: &#123;</span><br><span class="line">    *addTodo(&#123; <span class="attr">payload</span>: value &#125;, &#123; call, put, select &#125;) &#123;</span><br><span class="line">      <span class="comment">// 模拟网络请求</span></span><br><span class="line">      <span class="keyword">const</span> data = <span class="keyword">yield</span> call(todoService.query, value)</span><br><span class="line">      <span class="built_in">console</span>.log(data)</span><br><span class="line">      <span class="keyword">let</span> tempList = <span class="keyword">yield</span> select(<span class="function"><span class="params">state</span> =&gt;</span> state.todo.list)</span><br><span class="line">      <span class="keyword">let</span> list = []</span><br><span class="line">      list = list.concat(tempList)</span><br><span class="line">      <span class="keyword">const</span> tempObj = &#123;&#125;</span><br><span class="line">      tempObj.title = value</span><br><span class="line">      tempObj.id = list.length</span><br><span class="line">      tempObj.finished = <span class="literal">false</span></span><br><span class="line">      list.push(tempObj)</span><br><span class="line">      <span class="keyword">yield</span> put(&#123; <span class="attr">type</span>: <span class="string">'save'</span>, <span class="attr">payload</span>: &#123; list &#125;&#125;)</span><br><span class="line">    &#125;,</span><br><span class="line">    *toggle(&#123; <span class="attr">payload</span>: index &#125;, &#123; call, put, select &#125;) &#123;</span><br><span class="line">      <span class="comment">// 模拟网络请求</span></span><br><span class="line">      <span class="keyword">const</span> data = <span class="keyword">yield</span> call(todoService.query, index)</span><br><span class="line">      <span class="keyword">let</span> tempList = <span class="keyword">yield</span> select(<span class="function"><span class="params">state</span> =&gt;</span> state.todo.list)</span><br><span class="line">      <span class="keyword">let</span> list = []</span><br><span class="line">      list = list.concat(tempList)</span><br><span class="line">      <span class="keyword">let</span> obj = list[index]</span><br><span class="line">      obj.finished = !obj.finished</span><br><span class="line">      <span class="keyword">yield</span> put(&#123; <span class="attr">type</span>: <span class="string">'save'</span>, <span class="attr">payload</span>: &#123; list &#125; &#125;)</span><br><span class="line">    &#125;,</span><br><span class="line">    *<span class="keyword">delete</span>(&#123; <span class="attr">payload</span>: index &#125;, &#123; call, put, select &#125;) &#123;</span><br><span class="line">      <span class="keyword">const</span> data = <span class="keyword">yield</span> call(todoService.query, index)</span><br><span class="line">      <span class="keyword">let</span> tempList = <span class="keyword">yield</span> select(<span class="function"><span class="params">state</span> =&gt;</span> state.todo.list)</span><br><span class="line">      <span class="keyword">let</span> list = []</span><br><span class="line">      list = list.concat(tempList)</span><br><span class="line">      list.splice(index, <span class="number">1</span>)</span><br><span class="line">      <span class="keyword">yield</span> put(&#123; <span class="attr">type</span>: <span class="string">'save'</span>, <span class="attr">payload</span>: &#123; list &#125; &#125;)</span><br><span class="line">    &#125;,</span><br><span class="line">    *modify(&#123; <span class="attr">payload</span>: &#123; value, index &#125; &#125;, &#123; call, put, select &#125;) &#123;</span><br><span class="line">      <span class="keyword">const</span> data = <span class="keyword">yield</span> call(todoService.query, value)</span><br><span class="line">      <span class="keyword">let</span> tempList = <span class="keyword">yield</span> select(<span class="function"><span class="params">state</span> =&gt;</span> state.todo.list)</span><br><span class="line">      <span class="keyword">let</span> list = []</span><br><span class="line">      list = list.concat(tempList)</span><br><span class="line">      <span class="keyword">let</span> obj = list[index]</span><br><span class="line">      obj.title = value</span><br><span class="line">      <span class="keyword">yield</span> put(&#123; <span class="attr">type</span>: <span class="string">'save'</span>, <span class="attr">payload</span>: &#123; list &#125; &#125;)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><img src="https://upload-images.jianshu.io/upload_images/1505342-9ecb8ba2d8fd3ec2.png" alt></p>
<blockquote>
<p>在项目中最主要的会用到的是 <code>put</code> 与 <code>call</code></p>
</blockquote>
<p><strong>Subscription</strong></p>
<blockquote>
<ul>
<li>以 <code>key/value</code> 格式定义 <code>subscription</code>，<code>subscription</code> 是订阅，用于订阅一个数据源，然后根据需要 dispatch 相应的 action</li>
<li><code>subscription</code> 是订阅，用于订阅一个数据源，然后根据需要 <code>dispatch</code> 相应的<code>action</code>。在 <code>app.start()</code> 时被执行，数据源可以是当前的时间、当前页面的<code>url</code>、服务器的 <code>websocket</code> 连接、<code>history</code>路由变化等等。</li>
</ul>
</blockquote>
<ul>
<li><strong>注意</strong>：如果要使用 <code>app.unmodel()</code>，<code>subscription</code> 必须返回 <code>unlisten</code> 方法，用于取消数据订阅</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// subscriptions 写法</span></span><br><span class="line">subscriptions: &#123;</span><br><span class="line">    setup(&#123; dispatch, history &#125;) &#123;</span><br><span class="line">      <span class="comment">// 监听路由的变化，请求页面数据</span></span><br><span class="line">      <span class="keyword">return</span> history.listen(<span class="function">(<span class="params">&#123; pathname, search &#125;</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">const</span> query = queryString.parse(search)</span><br><span class="line">        <span class="keyword">let</span> list = []</span><br><span class="line">        <span class="keyword">if</span> (pathname === <span class="string">'todoList'</span>) &#123;</span><br><span class="line">          dispatch(&#123; <span class="attr">type</span>: <span class="string">'save'</span>, <span class="attr">payload</span>: &#123;list&#125; &#125;)</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure>
<h3 id="2-3-4-Router"><a href="#2-3-4-Router" class="headerlink" title="2.3.4 Router"></a>2.3.4 Router</h3><blockquote>
<p><code>Router</code> 表示路由配置信息，项目中的 <code>router.js</code></p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="function"><span class="keyword">function</span>(<span class="params">&#123; history &#125;</span>)</span>&#123;</span><br><span class="line">  <span class="keyword">return</span>(</span><br><span class="line">    &lt;Router history=&#123;history&#125;&gt;</span><br><span class="line">      &lt;Route path=<span class="string">"/"</span> component=&#123;App&#125; /&gt;</span><br><span class="line">    &lt;<span class="regexp">/Router&gt;</span></span><br><span class="line"><span class="regexp">  );</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br></pre></td></tr></table></figure>
<p><strong>RouteComponent</strong></p>
<blockquote>
<p><code>RouteComponent</code> 表示<code>Router</code> 里匹配路径的 <code>Component</code>，通常会绑定<code>model</code>的数据。如下:</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; connect &#125; <span class="keyword">from</span> <span class="string">'dva'</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">App</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">div</span>&gt;</span>App<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">mapStateToProps</span>(<span class="params">state</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> &#123; <span class="attr">todos</span>: state.todos &#125;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> connect(mapStateToProps)(App);</span><br></pre></td></tr></table></figure>
<h2 id="2-4-整体架构"><a href="#2-4-整体架构" class="headerlink" title="2.4 整体架构"></a>2.4 整体架构</h2><p> <img src="https://poetries1.gitee.io/img-repo/20191001/45.png" alt></p>
<ul>
<li>首先我们根据 <code>url</code> 访问相关的 <code>Route-Component</code>，在组件中我们通过 <code>dispatch</code>发送 <code>action</code> 到 <code>model</code> 里面的 <code>effect</code> 或者直接 <code>Reducer</code></li>
<li>当我们将<code>action</code>发送给<code>Effect</code>，基本上是取服务器上面请求数据的，服务器返回数据之后，<code>effect</code> 会发送相应的 <code>action</code>给 <code>reducer</code>，由唯一能改变 <code>state</code>的 <code>reducer</code> 改变 <code>state</code> ，然后通过<code>connect</code>重新渲染组件。</li>
<li>当我们将<code>action</code>发送给<code>reducer</code>，那直接由 <code>reducer</code> 改变 <code>state</code>，然后通过<code>connect</code>重新渲染组件</li>
</ul>
<h2 id="2-5-Dva图解"><a href="#2-5-Dva图解" class="headerlink" title="2.5 Dva图解"></a>2.5 Dva图解</h2><p><strong>图解一：加入Saga</strong></p>
<blockquote>
<p><code>React</code> 只负责页面渲染, 而不负责页面逻辑, 页面逻辑可以从中单独抽取出来, 变成 <code>store</code></p>
</blockquote>
<p> <img src="https://poetries1.gitee.io/img-repo/20191001/46.png" alt></p>
<blockquote>
<p>使用 <code>Middleware</code> 拦截 <code>action</code>, 这样一来异步的网络操作也就很方便了, 做成一个 <code>Middleware</code>就行了, 这里使用<code>redux-saga</code> 这个类库</p>
</blockquote>
<ul>
<li>点击创建 <code>Todo</code>的按钮, 发起一个 <code>type == addTodo</code> 的 <code>action</code></li>
<li><code>saga</code> 拦截这个 <code>action</code>, 发起 <code>http</code> 请求, 如果请求成功, 则继续向 <code>reducer</code> 发一个 <code>type == addTodoSucc</code> 的 <code>action</code>, 提示创建成功, 反之则发送 <code>type == addTodoFail</code> 的<code>action</code> 即可</li>
</ul>
<p><strong>图解二：Dva表示法</strong></p>
<p> <img src="https://poetries1.gitee.io/img-repo/20191001/47.png" alt></p>
<blockquote>
<p>dva做了 3 件很重要的事情</p>
</blockquote>
<ul>
<li>把 <code>store</code>及 <code>saga</code> 统一为一个 <code>model</code> 的概念, 写在一个 js 文件里面</li>
<li>增加了一个 <code>Subscriptions</code>, 用于收集其他来源的 <code>action</code>, eg: 键盘操作</li>
<li><code>model</code> 写法很简约, 类似于 <code>DSL</code> 或者 <code>RoR</code></li>
</ul>
<h1 id="三、计数器例子"><a href="#三、计数器例子" class="headerlink" title="三、计数器例子"></a>三、计数器例子</h1><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">$ dva new myapp</span><br></pre></td></tr></table></figure>
<p><strong>目录结构介绍</strong></p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">.</span><br><span class="line">├── mock    // mock数据文件夹</span><br><span class="line">├── node_modules // 第三方的依赖</span><br><span class="line">├── public  // 存放公共public文件的文件夹</span><br><span class="line">├── src  // 最重要的文件夹，编写代码都在这个文件夹下</span><br><span class="line">│   ├── assets // 可以放图片等公共资源</span><br><span class="line">│   ├── components // 就是react中的木偶组件</span><br><span class="line">│   ├── models // dva最重要的文件夹，所有的数据交互及逻辑都写在这里</span><br><span class="line">│   ├── routes // 就是react中的智能组件，不要被文件夹名字误导。</span><br><span class="line">│   ├── services // 放请求借口方法的文件夹</span><br><span class="line">│   ├── utils // 自己的工具方法可以放在这边</span><br><span class="line">│   ├── index.css // 入口文件样式</span><br><span class="line">│   ├── index.ejs // ejs模板引擎</span><br><span class="line">│   ├── index.js // 入口文件</span><br><span class="line">│   └── router.js // 项目的路由文件</span><br><span class="line">├── .eslintrc // bower安装目录的配置</span><br><span class="line">├── .editorconfig // 保证代码在不同编辑器可视化的工具</span><br><span class="line">├── .gitignore // git上传时忽略的文件</span><br><span class="line">├── .roadhogrc.js // 项目的配置文件，配置接口转发，css_module等都在这边。</span><br><span class="line">├── .roadhogrc.mock.js // 项目的配置文件</span><br><span class="line">└── package.json // 当前整一个项目的依赖</span><br></pre></td></tr></table></figure>
<p><strong>首先是前端的页面，我们使用 class 形式来创建组件，原例子中是使用无状态来创建的。react 创建组件的各种方式，大家可以看<a href="http://www.cnblogs.com/wonyun/p/5930333.html" target="_blank" rel="noopener">React创建组件的三种方式及其区别</a></strong></p>
<blockquote>
<p>我们先修改<code>route/IndexPage.js</code></p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; connect &#125; <span class="keyword">from</span> <span class="string">'dva'</span>;</span><br><span class="line"><span class="keyword">import</span> styles <span class="keyword">from</span> <span class="string">'./IndexPage.css'</span>;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">IndexPage</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>&#123;</span><br><span class="line">  render() &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123; dispatch &#125; = <span class="keyword">this</span>.props;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> (</span><br><span class="line">      &lt;div className=&#123;styles.normal&#125;&gt;</span><br><span class="line">        &lt;div className=&#123;styles.record&#125;&gt;Highest Record: <span class="number">1</span>&lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">        &lt;div className=&#123;styles.current&#125;&gt;2&lt;/</span>div&gt;</span><br><span class="line">        &lt;div className=&#123;styles.button&#125;&gt;</span><br><span class="line">          &lt;button onClick=&#123;() =&gt; &#123;&#125;&#125;&gt;+&lt;<span class="regexp">/button&gt;</span></span><br><span class="line"><span class="regexp">        &lt;/</span>div&gt;</span><br><span class="line">      &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">    );</span></span><br><span class="line"><span class="regexp">  &#125;</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">export default connect()(IndexPage);</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>同时修改样式<code>routes/IndexPage.css</code></p>
</blockquote>
<figure class="highlight css"><table><tr><td class="code"><pre><span class="line"><span class="selector-class">.normal</span> &#123;</span><br><span class="line">  <span class="attribute">width</span>: <span class="number">200px</span>;</span><br><span class="line">  <span class="attribute">margin</span>: <span class="number">100px</span> auto;</span><br><span class="line">  <span class="attribute">padding</span>: <span class="number">20px</span>;</span><br><span class="line">  <span class="attribute">border</span>: <span class="number">1px</span> solid <span class="number">#ccc</span>;</span><br><span class="line">  <span class="attribute">box-shadow</span>: <span class="number">0</span> <span class="number">0</span> <span class="number">20px</span> <span class="number">#ccc</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-class">.record</span> &#123;</span><br><span class="line">  <span class="attribute">border-bottom</span>: <span class="number">1px</span> solid <span class="number">#ccc</span>;</span><br><span class="line">  <span class="attribute">padding-bottom</span>: <span class="number">8px</span>;</span><br><span class="line">  <span class="attribute">color</span>: <span class="number">#ccc</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-class">.current</span> &#123;</span><br><span class="line">  <span class="attribute">text-align</span>: center;</span><br><span class="line">  <span class="attribute">font-size</span>: <span class="number">40px</span>;</span><br><span class="line">  <span class="attribute">padding</span>: <span class="number">40px</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-class">.button</span> &#123;</span><br><span class="line">  <span class="attribute">text-align</span>: center;</span><br><span class="line">  button &#123;</span><br><span class="line">    <span class="selector-tag">width</span>: 100<span class="selector-tag">px</span>;</span><br><span class="line">    <span class="selector-tag">height</span>: 40<span class="selector-tag">px</span>;</span><br><span class="line">    <span class="selector-tag">background</span>: <span class="selector-id">#aaa</span>;</span><br><span class="line">    <span class="selector-tag">color</span>: <span class="selector-id">#fff</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>在 <code>model</code> 处理<code>state</code>，在页面里面输出 <code>model</code> 中的 <code>state</code></p>
</blockquote>
<ul>
<li>首先我们在index.js中将<code>models/example.js</code>，即将model下一行的的注释打开</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> dva <span class="keyword">from</span> <span class="string">'dva'</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="string">'./index.css'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 1. Initialize</span></span><br><span class="line"><span class="keyword">const</span> app = dva();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. Plugins</span></span><br><span class="line"><span class="comment">// app.use(&#123;&#125;);</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 3. Model</span></span><br><span class="line">app.model(<span class="built_in">require</span>(<span class="string">'./models/example'</span>)); <span class="comment">// 打开注释</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 4. Router</span></span><br><span class="line">app.router(<span class="built_in">require</span>(<span class="string">'./router'</span>));</span><br><span class="line"></span><br><span class="line"><span class="comment">// 5. Start</span></span><br><span class="line">app.start(<span class="string">'#root'</span>);</span><br></pre></td></tr></table></figure>
<blockquote>
<p>接下来我们进入 <code>models/example.js</code>，将<code>namespace</code> 名字改为 <code>count</code>，<code>state</code>对象加上 <code>record</code> 与 <code>current</code> 属性。如下</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span><br><span class="line">  namespace: <span class="string">'count'</span>,</span><br><span class="line">  state: &#123;</span><br><span class="line">    record: <span class="number">0</span>,</span><br><span class="line">    current: <span class="number">0</span>,</span><br><span class="line">  &#125;,</span><br><span class="line">  ...</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>接着我们来到 <code>routes/indexpage.js</code> 页面，通过的 <code>mapStateToProps</code>引入相关的 <code>state</code></p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; connect &#125; <span class="keyword">from</span> <span class="string">'dva'</span>;</span><br><span class="line"><span class="keyword">import</span> styles <span class="keyword">from</span> <span class="string">'./IndexPage.css'</span>;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">IndexPage</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>&#123;</span><br><span class="line">  render() &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123; dispatch, count &#125; = <span class="keyword">this</span>.props;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> (</span><br><span class="line">      &lt;div className=&#123;styles.normal&#125;&gt;</span><br><span class="line">        &lt;div className=&#123;styles.record&#125;&gt;</span><br><span class="line">         Highest Record: &#123;count.record&#125; <span class="comment">// 将count的record输出</span></span><br><span class="line">        &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">        &lt;div className=&#123;styles.current&#125;&gt;</span></span><br><span class="line"><span class="regexp">         &#123;count.current&#125;</span></span><br><span class="line"><span class="regexp">        &lt;/</span>div&gt;</span><br><span class="line">        &lt;div className=&#123;styles.button&#125;&gt;</span><br><span class="line">          &lt;button onClick=&#123;() =&gt; &#123;&#125; &#125; &gt;</span><br><span class="line">                 +</span><br><span class="line">          &lt;<span class="regexp">/button&gt;</span></span><br><span class="line"><span class="regexp">        &lt;/</span>div&gt;</span><br><span class="line">      &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">    );</span></span><br><span class="line"><span class="regexp">  &#125;</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">function mapStateToProps(state) &#123;</span></span><br><span class="line"><span class="regexp">  return &#123; count: state.count &#125;;</span></span><br><span class="line"><span class="regexp">&#125; /</span><span class="regexp">/ 获取state</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">export default connect(mapStateToProps)(IndexPage);</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>通过 <code>+</code> 发送 <code>action</code>，通过 <code>reducer</code> 改变相应的 <code>state</code></p>
</blockquote>
<ul>
<li>首先我们在 <code>models/example.js</code>，写相应的 <code>reducer</code></li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span><br><span class="line">  ...</span><br><span class="line">  reducers: &#123;</span><br><span class="line">    add1(state) &#123;</span><br><span class="line">      <span class="keyword">const</span> newCurrent = state.current + <span class="number">1</span>;</span><br><span class="line">      <span class="keyword">return</span> &#123; ...state,</span><br><span class="line">        record: newCurrent &gt; state.record ? newCurrent : state.record,</span><br><span class="line">        current: newCurrent,</span><br><span class="line">      &#125;;</span><br><span class="line">    &#125;,</span><br><span class="line">    minus(state) &#123;</span><br><span class="line">      <span class="keyword">return</span> &#123; ...state, <span class="attr">current</span>: state.current - <span class="number">1</span> &#125;;</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>在页面的模板 <code>routes/IndexPage.js</code> 中 <code>+</code> 号点击的时候，<code>dispatch</code>一个 <code>action</code></p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; connect &#125; <span class="keyword">from</span> <span class="string">'dva'</span>;</span><br><span class="line"><span class="keyword">import</span> styles <span class="keyword">from</span> <span class="string">'./IndexPage.css'</span>;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">IndexPage</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>&#123;</span><br><span class="line">  render() &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123; dispatch, count &#125; = <span class="keyword">this</span>.props;</span><br><span class="line">    <span class="keyword">return</span> (</span><br><span class="line">      &lt;div className=&#123;styles.normal&#125;&gt;</span><br><span class="line">        &lt;div className=&#123;styles.record&#125;&gt;Highest Record: &#123;count.record&#125;&lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">        &lt;div className=&#123;styles.current&#125;&gt;&#123;count.current&#125;&lt;/</span>div&gt;</span><br><span class="line">        &lt;div className=&#123;styles.button&#125;&gt;</span><br><span class="line">          &lt;button </span><br><span class="line">+            onClick=&#123;() =&gt; &#123; dispatch(&#123; <span class="attr">type</span>: <span class="string">'count/add1'</span> &#125;);&#125;</span><br><span class="line">          &#125;&gt;+&lt;<span class="regexp">/button&gt;</span></span><br><span class="line"><span class="regexp">        &lt;/</span>div&gt;</span><br><span class="line">      &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">    );</span></span><br><span class="line"><span class="regexp">  &#125;</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br><span class="line"><span class="regexp">function mapStateToProps(state) &#123;</span></span><br><span class="line"><span class="regexp">  return &#123; count: state.count &#125;;</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">export default connect(mapStateToProps)(IndexPage);</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>接下来我们来使用 <code>effect</code> 模拟一个数据接口请求，返回之后，通过 <code>yield put()</code> 改变相应的 <code>state</code></p>
</blockquote>
<ul>
<li>首先我们替换相应的 <code>models/example.js</code> 的 <code>effect</code></li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">effects: &#123;</span><br><span class="line">    *add(action, &#123; call, put &#125;) &#123;</span><br><span class="line">      <span class="keyword">yield</span> call(delay, <span class="number">1000</span>);</span><br><span class="line">      <span class="keyword">yield</span> put(&#123; <span class="attr">type</span>: <span class="string">'minus'</span> &#125;);</span><br><span class="line">    &#125;,</span><br><span class="line">&#125;,</span><br></pre></td></tr></table></figure>
<blockquote>
<p>这里的 <code>delay</code>，是我这边写的一个延时的函数，我们在 <code>utils</code> 里面编写一个 <code>utils.js</code> ，一般请求接口的函数都会写在 <code>servers</code> 文件夹中</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">delay</span>(<span class="params">timeout</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve</span>) =&gt;</span> &#123;</span><br><span class="line">    setTimeout(resolve, timeout);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>订阅订阅键盘事件，使用 <code>subscriptions</code>，当用户按住 <code>command+up</code> 时候触发添加数字的 <code>action</code></p>
</blockquote>
<ul>
<li>在 <code>models/example.js</code> 中作如下修改</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">+<span class="keyword">import</span> key <span class="keyword">from</span> <span class="string">'keymaster'</span>;</span><br><span class="line">...</span><br><span class="line">app.model(&#123;</span><br><span class="line">  namespace: <span class="string">'count'</span>,</span><br><span class="line">+ subscriptions: &#123;</span><br><span class="line">+   keyboardWatcher(&#123; dispatch &#125;) &#123;</span><br><span class="line">+     key(<span class="string">'⌘+up, ctrl+up'</span>, () =&gt; &#123; dispatch(&#123;<span class="attr">type</span>:<span class="string">'add'</span>&#125;) &#125;);</span><br><span class="line">+   &#125;,</span><br><span class="line">+ &#125;,</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<ul>
<li>在这里你需要安装 <code>keymaster</code> 这个依赖</li>
</ul>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">npm install keymaster --save</span><br></pre></td></tr></table></figure>
<ul>
<li>现在你可以按住 <code>command+up</code> 就可以使 <code>current</code> 加1</li>
</ul>
<h1 id="四、Dva实践"><a href="#四、Dva实践" class="headerlink" title="四、Dva实践"></a>四、Dva实践</h1><h2 id="4-1-抽离Model"><a href="#4-1-抽离Model" class="headerlink" title="4.1 抽离Model"></a>4.1 抽离Model</h2><blockquote>
<p>抽离<code>Model</code>，根据设计页面需求，设计相应的<code>Model</code></p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// models/users.js</span></span><br><span class="line"><span class="comment">// version1: 从数据维度抽取，更适用于无状态的数据</span></span><br><span class="line"><span class="comment">// version2: 从业务状态抽取，将数据与组件的业务状态统一抽离成一个model</span></span><br><span class="line"><span class="comment">// 新增部分为在数据维度基础上，改为从业务状态抽取而添加的代码</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span><br><span class="line">  namespace: <span class="string">'users'</span>,</span><br><span class="line">  state: &#123;</span><br><span class="line">    list: [],</span><br><span class="line">    total: <span class="literal">null</span>,</span><br><span class="line">+   loading: <span class="literal">false</span>, <span class="comment">// 控制加载状态</span></span><br><span class="line">+   current: <span class="literal">null</span>, <span class="comment">// 当前分页信息</span></span><br><span class="line">+   currentItem: &#123;&#125;, <span class="comment">// 当前操作的用户对象</span></span><br><span class="line">+   modalVisible: <span class="literal">false</span>, <span class="comment">// 弹出窗的显示状态</span></span><br><span class="line">+   modalType: <span class="string">'create'</span>, <span class="comment">// 弹出窗的类型（添加用户，编辑用户）</span></span><br><span class="line">  &#125;,</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 异步操作</span></span><br><span class="line">    effects: &#123;</span><br><span class="line">        *query()&#123;&#125;,</span><br><span class="line">        *create()&#123;&#125;,</span><br><span class="line">        *<span class="string">'delete'</span>()&#123;&#125;,   <span class="comment">// 因为delete是关键字，特殊处理</span></span><br><span class="line">        *update()&#123;&#125;,</span><br><span class="line">    &#125;,</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 替换状态树</span></span><br><span class="line">    reducers: &#123;</span><br><span class="line">+       showLoading()&#123;&#125;, <span class="comment">// 控制加载状态的 reducer</span></span><br><span class="line">+       showModel()&#123;&#125;, <span class="comment">// 控制 Model 显示状态的 reducer</span></span><br><span class="line">+       hideModel()&#123;&#125;,</span><br><span class="line">        querySuccess()&#123;&#125;,</span><br><span class="line">        createSuccess()&#123;&#125;,</span><br><span class="line">        deleteSuccess()&#123;&#125;,</span><br><span class="line">        updateSuccess()&#123;&#125;,</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="4-2-设计组件"><a href="#4-2-设计组件" class="headerlink" title="4.2 设计组件"></a>4.2 设计组件</h2><blockquote>
<p>先设置容器组件的访问路径，再创建组件文件</p>
</blockquote>
<h3 id="4-2-1-容器组件"><a href="#4-2-1-容器组件" class="headerlink" title="4.2.1 容器组件"></a>4.2.1 容器组件</h3><blockquote>
<p>具有监听数据行为的组件，职责是绑定相关联的 model 数据，包含子组件；传入的数据来源于model</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> React, &#123; Component, PropTypes &#125; <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// dva 的 connect 方法可以将组件和数据关联在一起</span></span><br><span class="line"><span class="keyword">import</span> &#123; connect &#125; <span class="keyword">from</span> <span class="string">'dva'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 组件本身</span></span><br><span class="line"><span class="keyword">const</span> MyComponent = <span class="function">(<span class="params">props</span>)=&gt;</span>&#123;&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// propTypes属性，用于限制props的传入数据类型</span></span><br><span class="line">MyComponent.propTypes = &#123;&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 声明模型传递函数，用于建立组件和数据的映射关系</span></span><br><span class="line"><span class="comment">// 实际表示 将ModelA这一个数据模型，绑定到当前的组件中，则在当前组件中，随时可以取到ModelA的最新值</span></span><br><span class="line"><span class="comment">// 可以绑定多个Model</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">mapStateToProps</span>(<span class="params">&#123;ModelA&#125;</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> &#123;ModelA&#125;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 关联 model</span></span><br><span class="line"><span class="comment">// 正式调用模型传递函数，完成模型绑定</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> connect(mapStateToProps)(MyComponent);</span><br></pre></td></tr></table></figure>
<h3 id="4-2-2-展示组件"><a href="#4-2-2-展示组件" class="headerlink" title="4.2.2 展示组件"></a>4.2.2 展示组件</h3><blockquote>
<p>展示通过 <code>props</code> 传递到组件内部数据；传入的数据来源于容器组件向展示组件的<code>props</code></p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> React, &#123; Component, PropTypes &#125; <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 组件本身</span></span><br><span class="line"><span class="comment">// 所需要的数据通过 Container Component 通过 props 传递下来</span></span><br><span class="line"><span class="keyword">const</span> MyComponent = <span class="function">(<span class="params">props</span>)=&gt;</span>&#123;&#125;</span><br><span class="line">MyComponent.propTypes = &#123;&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 并不会监听数据</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> MyComponent;</span><br></pre></td></tr></table></figure>
<h3 id="4-2-3-设置路由"><a href="#4-2-3-设置路由" class="headerlink" title="4.2.3 设置路由"></a>4.2.3 设置路由</h3><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// .src/router.js</span></span><br><span class="line"><span class="keyword">import</span> React, &#123; PropTypes &#125; <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; Router, Route &#125; <span class="keyword">from</span> <span class="string">'dva/router'</span>;</span><br><span class="line"><span class="keyword">import</span> Users <span class="keyword">from</span> <span class="string">'./routes/Users'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="function"><span class="keyword">function</span>(<span class="params">&#123; history &#125;</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    &lt;Router history=&#123;history&#125;&gt;</span><br><span class="line">      &lt;Route path=<span class="string">"/users"</span> component=&#123;Users&#125; /&gt;</span><br><span class="line">    &lt;<span class="regexp">/Router&gt;</span></span><br><span class="line"><span class="regexp">  );</span></span><br><span class="line"><span class="regexp">&#125;;</span></span><br></pre></td></tr></table></figure>
<p><strong>容器组件雏形</strong></p>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// .src/routes/Users.jsx</span></span><br><span class="line"><span class="keyword">import</span> React, &#123; PropTypes &#125; <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Users</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    &lt;div&gt;User Router Component&lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">  );</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">export default Users;</span></span><br></pre></td></tr></table></figure>
<h3 id="4-2-4-设计容器组件"><a href="#4-2-4-设计容器组件" class="headerlink" title="4.2.4 设计容器组件"></a>4.2.4 设计容器组件</h3><blockquote>
<p>自顶向下的设计方法：先设计容器组件，再逐步细化内部的展示容器</p>
</blockquote>
<p>组件的定义方式</p>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 方法一： es6 的写法，当组件设计react生命周期时，可采用这种写法</span></span><br><span class="line"><span class="comment">// 具有生命周期的组件，可以在接收到传入数据变化时，自定义执行方法，有自己的行为模式</span></span><br><span class="line"><span class="comment">// 比如在组件生成后调用xx请求(componentDidMount)、可以自己决定要不要更新渲染(shouldComponentUpdate)等</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">App</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span>(</span>&#123;&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 方法二： stateless 的写法，定义无状态组件</span></span><br><span class="line"><span class="comment">// 无状态组件，仅仅根据传入的数据更新，修改自己的渲染内容</span></span><br><span class="line"><span class="keyword">const</span> App = <span class="function">(<span class="params">props</span>) =&gt;</span> (&#123;&#125;);</span><br></pre></td></tr></table></figure>
<p>容器组件：</p>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// ./src/routes/Users.jsx</span></span><br><span class="line"><span class="keyword">import</span> React, &#123; Component, PropTypes &#125; <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 引入展示组件 （暂时都没实现）</span></span><br><span class="line"><span class="keyword">import</span> UserList <span class="keyword">from</span> <span class="string">'../components/Users/UserList'</span>;</span><br><span class="line"><span class="keyword">import</span> UserSearch <span class="keyword">from</span> <span class="string">'../components/Users/UserSearch'</span>;</span><br><span class="line"><span class="keyword">import</span> UserModal <span class="keyword">from</span> <span class="string">'../components/Users/UserModal'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 引入css样式表</span></span><br><span class="line"><span class="keyword">import</span> styles <span class="keyword">from</span> <span class="string">'./style.less'</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Users</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 向userListProps中传入静态数据</span></span><br><span class="line">  <span class="keyword">const</span> userSearchProps = &#123;&#125;;</span><br><span class="line">  <span class="keyword">const</span> userListProps = &#123;</span><br><span class="line">    total: <span class="number">3</span>,</span><br><span class="line">    current: <span class="number">1</span>,</span><br><span class="line">    loading: <span class="literal">false</span>,</span><br><span class="line">    dataSource: [</span><br><span class="line">      &#123;</span><br><span class="line">        name: <span class="string">'张三'</span>,</span><br><span class="line">        age: <span class="number">23</span>,</span><br><span class="line">        address: <span class="string">'成都'</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">      &#123;</span><br><span class="line">        name: <span class="string">'李四'</span>,</span><br><span class="line">        age: <span class="number">24</span>,</span><br><span class="line">        address: <span class="string">'杭州'</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">      &#123;</span><br><span class="line">        name: <span class="string">'王五'</span>,</span><br><span class="line">        age: <span class="number">25</span>,</span><br><span class="line">        address: <span class="string">'上海'</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">    ],</span><br><span class="line">  &#125;;</span><br><span class="line">  <span class="keyword">const</span> userModalProps = &#123;&#125;;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    &lt;div className=&#123;styles.normal&#125;&gt;</span><br><span class="line">      &#123;<span class="comment">/* 用户筛选搜索框 */</span>&#125;</span><br><span class="line">      &lt;UserSearch &#123;...userSearchProps&#125; /&gt;</span><br><span class="line">      &#123;<span class="comment">/* 用户信息展示列表 */</span>&#125;</span><br><span class="line">      &lt;UserList &#123;...userListProps&#125; /&gt;</span><br><span class="line">      &#123;<span class="comment">/* 添加用户 &amp; 修改用户弹出的浮层 */</span>&#125;</span><br><span class="line">      &lt;UserModal &#123;...userModalProps&#125; /&gt;</span><br><span class="line">    &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">  );</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">/</span><span class="regexp">/ 很关键的对外输出export；使外部可通过import引用使用此组件</span></span><br><span class="line"><span class="regexp">export default Users;</span></span><br></pre></td></tr></table></figure>
<p>展示组件UserList</p>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// ./src/components/Users/UserList.jsx</span></span><br><span class="line"><span class="keyword">import</span> React, &#123; Component, PropTypes &#125; <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 采用antd的UI组件</span></span><br><span class="line"><span class="keyword">import</span> &#123; Table, message, Popconfirm &#125; <span class="keyword">from</span> <span class="string">'antd'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 采用 stateless 的写法</span></span><br><span class="line"><span class="keyword">const</span> UserList = (&#123;</span><br><span class="line">    total,</span><br><span class="line">    current,</span><br><span class="line">    loading,</span><br><span class="line">    dataSource,</span><br><span class="line">&#125;) =&gt; &#123;</span><br><span class="line">  <span class="keyword">const</span> columns = [&#123;</span><br><span class="line">    title: <span class="string">'姓名'</span>,</span><br><span class="line">    dataIndex: <span class="string">'name'</span>,</span><br><span class="line">    key: <span class="string">'name'</span>,</span><br><span class="line">    render: <span class="function">(<span class="params">text</span>) =&gt;</span> <span class="xml"><span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">"#"</span>&gt;</span>&#123;text&#125;<span class="tag">&lt;/<span class="name">a</span>&gt;</span></span>,</span><br><span class="line">  &#125;, &#123;</span><br><span class="line">    title: <span class="string">'年龄'</span>,</span><br><span class="line">    dataIndex: <span class="string">'age'</span>,</span><br><span class="line">    key: <span class="string">'age'</span>,</span><br><span class="line">  &#125;, &#123;</span><br><span class="line">    title: <span class="string">'住址'</span>,</span><br><span class="line">    dataIndex: <span class="string">'address'</span>,</span><br><span class="line">    key: <span class="string">'address'</span>,</span><br><span class="line">  &#125;, &#123;</span><br><span class="line">    title: <span class="string">'操作'</span>,</span><br><span class="line">    key: <span class="string">'operation'</span>,</span><br><span class="line">    render: <span class="function">(<span class="params">text, record</span>) =&gt;</span> (</span><br><span class="line">      &lt;p&gt;</span><br><span class="line">        &lt;a onClick=&#123;()=&gt;&#123;&#125;&#125;&gt;编辑&lt;<span class="regexp">/a&gt;</span></span><br><span class="line"><span class="regexp">         </span></span><br><span class="line"><span class="regexp">        &lt;Popconfirm title="确定要删除吗？" onConfirm=&#123;()=&gt;&#123;&#125;&#125;&gt;</span></span><br><span class="line"><span class="regexp">          &lt;a&gt;删除&lt;/</span>a&gt;</span><br><span class="line">        &lt;<span class="regexp">/Popconfirm&gt;</span></span><br><span class="line"><span class="regexp">      &lt;/</span>p&gt;</span><br><span class="line">    ),</span><br><span class="line">  &#125;];</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 定义分页对象</span></span><br><span class="line">  <span class="keyword">const</span> pagination = &#123;</span><br><span class="line">    total,</span><br><span class="line">    current,</span><br><span class="line">    pageSize: <span class="number">10</span>,</span><br><span class="line">    onChange: <span class="function"><span class="params">()</span>=&gt;</span>&#123;&#125;,</span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">  <span class="comment">// 此处的Table标签使用了antd组件，传入的参数格式是由antd组件库本身决定的</span></span><br><span class="line">  <span class="comment">// 此外还需要在index.js中引入antd  import 'antd/dist/antd.css'</span></span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">      &lt;Table</span><br><span class="line">        columns=&#123;columns&#125;</span><br><span class="line">        dataSource=&#123;dataSource&#125;</span><br><span class="line">        loading=&#123;loading&#125;</span><br><span class="line">        rowKey=&#123;record =&gt; record.id&#125;</span><br><span class="line">        pagination=&#123;pagination&#125;</span><br><span class="line">      /&gt;</span><br><span class="line">    &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">  );</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">export default UserList;</span></span><br></pre></td></tr></table></figure>
<h2 id="4-3-添加Reducer"><a href="#4-3-添加Reducer" class="headerlink" title="4.3 添加Reducer"></a>4.3 添加Reducer</h2><blockquote>
<p>在整个应用中，只有<code>model</code>中的<code>reducer</code>函数可以直接修改自己所在<code>model</code>的<code>state</code>参数，其余都是非法操作；<br>并且必须使用<code>return {...state}</code>的形式进行修改</p>
</blockquote>
<h3 id="4-3-1-第一步：实现reducer函数"><a href="#4-3-1-第一步：实现reducer函数" class="headerlink" title="4.3.1 第一步：实现reducer函数"></a>4.3.1 第一步：实现reducer函数</h3><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// models/users.js</span></span><br><span class="line"><span class="comment">// 使用静态数据返回，把userList中的静态数据移到此处</span></span><br><span class="line"><span class="comment">// querySuccess这个action的作用在于，修改了model的数据</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span><br><span class="line">  namespace: <span class="string">'users'</span>,</span><br><span class="line">  state： &#123;&#125;，</span><br><span class="line">  subscriptions: &#123;&#125;,</span><br><span class="line">  effects: &#123;&#125;,</span><br><span class="line">  reducers: &#123;</span><br><span class="line">    querySuccess(state)&#123;</span><br><span class="line">        <span class="keyword">const</span> mock = &#123;</span><br><span class="line">          total: <span class="number">3</span>,</span><br><span class="line">          current: <span class="number">1</span>,</span><br><span class="line">          loading: <span class="literal">false</span>,</span><br><span class="line">          list: [</span><br><span class="line">            &#123;</span><br><span class="line">              id: <span class="number">1</span>,</span><br><span class="line">              name: <span class="string">'张三'</span>,</span><br><span class="line">              age: <span class="number">23</span>,</span><br><span class="line">              address: <span class="string">'成都'</span>,</span><br><span class="line">            &#125;,</span><br><span class="line">            &#123;</span><br><span class="line">              id: <span class="number">2</span>,</span><br><span class="line">              name: <span class="string">'李四'</span>,</span><br><span class="line">              age: <span class="number">24</span>,</span><br><span class="line">              address: <span class="string">'杭州'</span>,</span><br><span class="line">            &#125;,</span><br><span class="line">            &#123;</span><br><span class="line">              id: <span class="number">3</span>,</span><br><span class="line">              name: <span class="string">'王五'</span>,</span><br><span class="line">              age: <span class="number">25</span>,</span><br><span class="line">              address: <span class="string">'上海'</span>,</span><br><span class="line">            &#125;,</span><br><span class="line">          ]</span><br><span class="line">        &#125;;</span><br><span class="line">        <span class="comment">// return 的内容是一个对象，涵盖原state中的所有属性，以实现“更新替换”的效果</span></span><br><span class="line">        <span class="keyword">return</span> &#123;...state, ...mock, <span class="attr">loading</span>: <span class="literal">false</span>&#125;;</span><br><span class="line">      &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="4-3-2-第二步：关联Model中的数据源"><a href="#4-3-2-第二步：关联Model中的数据源" class="headerlink" title="4.3.2 第二步：关联Model中的数据源"></a>4.3.2 第二步：关联Model中的数据源</h3><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// routes/Users.jsx</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> React, &#123; PropTypes &#125; <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 最后用到了connect函数，需要在头部预先引入connect</span></span><br><span class="line"><span class="keyword">import</span> &#123; connect &#125; <span class="keyword">from</span> <span class="string">'dva'</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Users</span>(<span class="params">&#123; location, dispatch, users &#125;</span>) </span>&#123;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> &#123;</span><br><span class="line">    loading, list, total, current,</span><br><span class="line">    currentItem, modalVisible, modalType</span><br><span class="line">    &#125; = users;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> userSearchProps=&#123;&#125;;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 使用传入的数据源，进行数据渲染</span></span><br><span class="line">  <span class="keyword">const</span> userListProps=&#123;</span><br><span class="line">    dataSource: list,</span><br><span class="line">    total,</span><br><span class="line">    loading,</span><br><span class="line">    current,</span><br><span class="line">  &#125;;</span><br><span class="line">  <span class="keyword">const</span> userModalProps=&#123;&#125;;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    &lt;div className=&#123;styles.normal&#125;&gt;</span><br><span class="line">      &#123;<span class="comment">/* 用户筛选搜索框 */</span>&#125;</span><br><span class="line">      &lt;UserSearch &#123;...userSearchProps&#125; /&gt;</span><br><span class="line">      &#123;<span class="comment">/* 用户信息展示列表 */</span>&#125;</span><br><span class="line">      &lt;UserList &#123;...userListProps&#125; /&gt;</span><br><span class="line">      &#123;<span class="comment">/* 添加用户 &amp; 修改用户弹出的浮层 */</span>&#125;</span><br><span class="line">      &lt;UserModal &#123;...userModalProps&#125; /&gt;</span><br><span class="line">    &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">  );</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">/</span><span class="regexp">/ 声明组件的props类型</span></span><br><span class="line"><span class="regexp">Users.propTypes = &#123;</span></span><br><span class="line"><span class="regexp">  users: PropTypes.object,</span></span><br><span class="line"><span class="regexp">&#125;;</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">/</span><span class="regexp">/ 指定订阅数据，并且关联到users中</span></span><br><span class="line"><span class="regexp">function mapStateToProps(&#123; users &#125;) &#123;</span></span><br><span class="line"><span class="regexp">  return &#123;users&#125;;</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">/</span><span class="regexp">/ 建立数据关联关系</span></span><br><span class="line"><span class="regexp">export default connect(mapStateToProps)(Users);</span></span><br></pre></td></tr></table></figure>
<h3 id="4-3-3-第三步：通过发起Action，在组件中获取Model中的数据"><a href="#4-3-3-第三步：通过发起Action，在组件中获取Model中的数据" class="headerlink" title="4.3.3 第三步：通过发起Action，在组件中获取Model中的数据"></a>4.3.3 第三步：通过发起Action，在组件中获取Model中的数据</h3><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// models/users.js</span></span><br><span class="line"><span class="comment">// 在组件生成后发出action，示例：</span></span><br><span class="line">componentDidMount() &#123;</span><br><span class="line">  <span class="keyword">this</span>.props.dispatch(&#123;</span><br><span class="line">    type: <span class="string">'model/action'</span>,     <span class="comment">// type对应action的名字</span></span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 在本次实践中，在访问/users/路由时，就是我们获取用户数据的时机</span></span><br><span class="line"><span class="comment">// 因此把dispatch移至subscription中</span></span><br><span class="line"><span class="comment">// subcription，订阅(或是监听)一个数据源，然后根据条件dispatch对应的action</span></span><br><span class="line"><span class="comment">// 数据源可以是当前的时间、服务器的 websocket 连接、keyboard 输入、geolocation 变化、history 路由变化等等  </span></span><br><span class="line"><span class="comment">// 此处订阅的数据源就是路由信息，当路由为/users，则派发'querySuccess'这个effects方法</span></span><br><span class="line">subscriptions: &#123;</span><br><span class="line">    setup(&#123; dispatch, history &#125;) &#123;</span><br><span class="line">      history.listen(<span class="function"><span class="params">location</span> =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (location.pathname === <span class="string">'/users'</span>) &#123;</span><br><span class="line">          dispatch(&#123;</span><br><span class="line">            type: <span class="string">'querySuccess'</span>,</span><br><span class="line">            payload: &#123;&#125;</span><br><span class="line">          &#125;);</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br></pre></td></tr></table></figure>
<h3 id="4-3-4-第四步：-在index-js中添加models"><a href="#4-3-4-第四步：-在index-js中添加models" class="headerlink" title="4.3.4 第四步： 在index.js中添加models"></a>4.3.4 第四步： 在index.js中添加models</h3><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// model必须在此完成注册，才能全局有效</span></span><br><span class="line"><span class="comment">// index.js</span></span><br><span class="line">app.model(<span class="built_in">require</span>(<span class="string">'./models/users.js'</span>));</span><br></pre></td></tr></table></figure>
<h3 id="4-4-添加Effects"><a href="#4-4-添加Effects" class="headerlink" title="4.4 添加Effects"></a>4.4 添加Effects</h3><blockquote>
<p><code>Effects</code>的作用在于处理异步函数，控制数据流程。<br>因为在真实场景中，数据都来自服务器，需要在发起异步请求获得返回值后再设置数据，更新<code>state</code>。<br>因此我们往往在<code>Effects</code>中调用<code>reducer</code></p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span><br><span class="line">  namespace: <span class="string">'users'</span>,</span><br><span class="line">  state： &#123;&#125;，</span><br><span class="line">  subscriptions: &#123;&#125;,</span><br><span class="line">  effects: &#123;</span><br><span class="line">    <span class="comment">// 添加effects函数</span></span><br><span class="line">    <span class="comment">// call与put是dva的函数</span></span><br><span class="line">    <span class="comment">// call调用执行一个函数</span></span><br><span class="line">    <span class="comment">// put则是dispatch执行一个action</span></span><br><span class="line">    <span class="comment">// select用于访问其他model</span></span><br><span class="line">    *query(&#123; payload &#125;, &#123; select, call, put &#125;) &#123;</span><br><span class="line">        <span class="keyword">yield</span> put(&#123; <span class="attr">type</span>: <span class="string">'showLoading'</span> &#125;);</span><br><span class="line">        <span class="keyword">const</span> &#123; data &#125; = <span class="keyword">yield</span> call(query);</span><br><span class="line">        <span class="keyword">if</span> (data) &#123;</span><br><span class="line">          <span class="keyword">yield</span> put(&#123;</span><br><span class="line">            type: <span class="string">'querySuccess'</span>,</span><br><span class="line">            payload: &#123;</span><br><span class="line">              list: data.data,</span><br><span class="line">              total: data.page.total,</span><br><span class="line">              current: data.page.current</span><br><span class="line">            &#125;</span><br><span class="line">          &#125;);</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">  reducers: &#123;&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 添加请求处理   包含了一个ajax请求</span></span><br><span class="line"><span class="comment">// models/users.js</span></span><br><span class="line"><span class="keyword">import</span> request <span class="keyword">from</span> <span class="string">'../utils/request'</span>;</span><br><span class="line"><span class="keyword">import</span> qs <span class="keyword">from</span> <span class="string">'qs'</span>;</span><br><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">function</span> <span class="title">query</span>(<span class="params">params</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> request(<span class="string">`/api/users?<span class="subst">$&#123;qs.stringify(params)&#125;</span>`</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="4-5-把请求处理分离到service中"><a href="#4-5-把请求处理分离到service中" class="headerlink" title="4.5 把请求处理分离到service中"></a>4.5 把请求处理分离到service中</h3><blockquote>
<p>用意在于分离(可复用的)ajax请求</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// services/users.js</span></span><br><span class="line"><span class="keyword">import</span> request <span class="keyword">from</span> <span class="string">'../utils/request'</span>;</span><br><span class="line"><span class="keyword">import</span> qs <span class="keyword">from</span> <span class="string">'qs'</span>;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">async</span> <span class="function"><span class="keyword">function</span> <span class="title">query</span>(<span class="params">params</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> request(<span class="string">`/api/users?<span class="subst">$&#123;qs.stringify(params)&#125;</span>`</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 在models中引用</span></span><br><span class="line"><span class="comment">// models/users.js</span></span><br><span class="line"><span class="keyword">import</span> &#123;query&#125; <span class="keyword">from</span> <span class="string">'../services/users'</span>;</span><br></pre></td></tr></table></figure>
<h2 id="五、使用dva框架和直接使用redux写法的区别"><a href="#五、使用dva框架和直接使用redux写法的区别" class="headerlink" title="五、使用dva框架和直接使用redux写法的区别"></a>五、使用dva框架和直接使用redux写法的区别</h2><h3 id="5-1-使用-redux"><a href="#5-1-使用-redux" class="headerlink" title="5.1 使用 redux"></a>5.1 使用 redux</h3><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// action.js</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> REQUEST_TODO = <span class="string">'REQUEST_TODO'</span>;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> RESPONSE_TODO = <span class="string">'RESPONSE_TODO'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> request = <span class="function"><span class="params">count</span> =&gt;</span> (&#123;<span class="attr">type</span>: REQUEST_TODO, <span class="attr">payload</span>: &#123;<span class="attr">loading</span>: <span class="literal">true</span>, count&#125;&#125;);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> response = <span class="function"><span class="params">count</span> =&gt;</span> (&#123;<span class="attr">type</span>: RESPONSE_TODO, <span class="attr">payload</span>: &#123;<span class="attr">loading</span>: <span class="literal">false</span>, count&#125;&#125;);</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> fetch = <span class="function"><span class="params">count</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="function">(<span class="params">dispatch</span>) =&gt;</span> &#123;</span><br><span class="line">    dispatch(request(count));</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="params">resolve</span> =&gt;</span> &#123;</span><br><span class="line">      setTimeout(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">        resolve(count + <span class="number">1</span>);</span><br><span class="line">      &#125;, <span class="number">1000</span>)</span><br><span class="line">    &#125;).then(<span class="function"><span class="params">data</span> =&gt;</span> &#123;</span><br><span class="line">      dispatch(response(data))</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">//reducer.js</span></span><br><span class="line"><span class="keyword">import</span> &#123; REQUEST_TODO, RESPONSE_TODO &#125; <span class="keyword">from</span> <span class="string">'./actions'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> (state = &#123;</span><br><span class="line">  loading: <span class="literal">false</span>,</span><br><span class="line">  count: <span class="number">0</span></span><br><span class="line">&#125;, action) =&gt; &#123;</span><br><span class="line">  <span class="keyword">switch</span> (action.type) &#123;</span><br><span class="line">    <span class="keyword">case</span> REQUEST_TODO:</span><br><span class="line">      <span class="keyword">return</span> &#123;...state, ...action.payload&#125;;</span><br><span class="line">    <span class="keyword">case</span> RESPONSE_TODO:</span><br><span class="line">      <span class="keyword">return</span> &#123;...state, ...action.payload&#125;;</span><br><span class="line">    <span class="keyword">default</span>:</span><br><span class="line">      <span class="keyword">return</span> state;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">// app.js</span><br><span class="line">import React from &apos;react&apos;;</span><br><span class="line">import &#123; bindActionCreators &#125; from &apos;redux&apos;;</span><br><span class="line">import &#123; connect &#125; from &apos;react-redux&apos;;</span><br><span class="line"></span><br><span class="line">import * as actions from &apos;./actions&apos;;</span><br><span class="line"></span><br><span class="line">const App = (&#123;fetch, count, loading&#125;) =&gt; &#123;</span><br><span class="line">  return (</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">      &#123;loading ? &lt;div&gt;loading...&lt;/div&gt; : &lt;div&gt;&#123;count&#125;&lt;/div&gt;&#125;</span><br><span class="line">      &lt;button onClick=&#123;() =&gt; fetch(count)&#125;&gt;add&lt;/button&gt;</span><br><span class="line">    &lt;/div&gt;</span><br><span class="line">  )</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">function mapStateToProps(state) &#123;</span><br><span class="line">  return state;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">function mapDispatchToProps(dispatch) &#123;</span><br><span class="line">  return bindActionCreators(actions, dispatch)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">export default connect(mapStateToProps, mapDispatchToProps)(App)</span><br></pre></td></tr></table></figure>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">//index.js</span></span><br><span class="line"><span class="keyword">import</span> &#123; render &#125; <span class="keyword">from</span> <span class="string">'react-dom'</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; createStore, applyMiddleware &#125; <span class="keyword">from</span> <span class="string">'redux'</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; Provider &#125; <span class="keyword">from</span> <span class="string">'react-redux'</span></span><br><span class="line"><span class="keyword">import</span> thunkMiddleware <span class="keyword">from</span> <span class="string">'redux-thunk'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> reducer <span class="keyword">from</span> <span class="string">'./app/reducer'</span>;</span><br><span class="line"><span class="keyword">import</span> App <span class="keyword">from</span> <span class="string">'./app/app'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> store = createStore(reducer, applyMiddleware(thunkMiddleware));</span><br><span class="line"></span><br><span class="line">render(</span><br><span class="line">  &lt;Provider store=&#123;store&#125;&gt;</span><br><span class="line">    &lt;App/&gt;</span><br><span class="line">  &lt;<span class="regexp">/Provider&gt;</span></span><br><span class="line"><span class="regexp">  ,</span></span><br><span class="line"><span class="regexp">  document.getElementById('app')</span></span><br><span class="line"><span class="regexp">)</span></span><br></pre></td></tr></table></figure>
<h3 id="5-2-使用dva"><a href="#5-2-使用dva" class="headerlink" title="5.2 使用dva"></a>5.2 使用dva</h3><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line">// model.js</span><br><span class="line">export default &#123;</span><br><span class="line">  namespace: &apos;demo&apos;,</span><br><span class="line">  state: &#123;</span><br><span class="line">    loading: false,</span><br><span class="line">    count: 0</span><br><span class="line">  &#125;,</span><br><span class="line">  reducers: &#123;</span><br><span class="line">    request(state, payload) &#123;</span><br><span class="line">      return &#123;...state, ...payload&#125;;</span><br><span class="line">    &#125;,</span><br><span class="line">    response(state, payload) &#123;</span><br><span class="line">      return &#123;...state, ...payload&#125;;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;,</span><br><span class="line">  effects: &#123;</span><br><span class="line">    *&apos;fetch&apos;(action, &#123;put, call&#125;) &#123;</span><br><span class="line">      yield put(&#123;type: &apos;request&apos;, loading: true&#125;);</span><br><span class="line"></span><br><span class="line">      let count = yield call((count) =&gt; &#123;</span><br><span class="line">        return new Promise(resolve =&gt; &#123;</span><br><span class="line">          setTimeout(() =&gt; &#123;</span><br><span class="line">            resolve(count + 1);</span><br><span class="line">          &#125;, 1000);</span><br><span class="line">        &#125;);</span><br><span class="line">      &#125;, action.count);</span><br><span class="line"></span><br><span class="line">      yield put(&#123;</span><br><span class="line">        type: &apos;response&apos;,</span><br><span class="line">        loading: false,</span><br><span class="line">        count</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">//app.js</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span></span><br><span class="line"><span class="keyword">import</span> &#123; connect &#125; <span class="keyword">from</span> <span class="string">'dva'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> App = <span class="function">(<span class="params">&#123;fetch, count, loading&#125;</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">      &#123;loading ? <span class="xml"><span class="tag">&lt;<span class="name">div</span>&gt;</span>loading...<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span> : <span class="xml"><span class="tag">&lt;<span class="name">div</span>&gt;</span>&#123;count&#125;<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span>&#125;</span><br><span class="line">      &lt;button onClick=&#123;() =&gt; fetch(count)&#125;&gt;add&lt;<span class="regexp">/button&gt;</span></span><br><span class="line"><span class="regexp">    &lt;/</span>div&gt;</span><br><span class="line">  )</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">mapStateToProps</span>(<span class="params">state</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> state.demo;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">mapDispatchToProps</span>(<span class="params">dispatch</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    fetch(count)&#123;</span><br><span class="line">      dispatch(&#123;<span class="attr">type</span>: <span class="string">'demo/fetch'</span>, count&#125;);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> connect(mapStateToProps, mapDispatchToProps)(App)</span><br></pre></td></tr></table></figure>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// index.js</span></span><br><span class="line"><span class="keyword">import</span> dva <span class="keyword">from</span> <span class="string">'dva'</span>;</span><br><span class="line"><span class="keyword">import</span> model <span class="keyword">from</span> <span class="string">'./model'</span>;</span><br><span class="line"><span class="keyword">import</span> App <span class="keyword">from</span> <span class="string">'./app'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> app = dva();</span><br><span class="line"></span><br><span class="line">app.use(&#123;&#125;);</span><br><span class="line"></span><br><span class="line">app.model(model);</span><br><span class="line"></span><br><span class="line">app.router(<span class="function"><span class="params">()</span> =&gt;</span> <span class="xml"><span class="tag">&lt;<span class="name">App</span> /&gt;</span></span>);</span><br><span class="line"></span><br><span class="line">app.start();</span><br></pre></td></tr></table></figure>
<ul>
<li>使用 <code>redux</code> 需要拆分出<code>action</code>模块和<code>reducer</code>模块</li>
<li><code>dva</code>将<code>action</code>和<code>reducer</code>封装到<code>model</code>中，异步流程采用<code>Generator</code>处理</li>
</ul>
<h1 id="六、使用axios统一处理"><a href="#六、使用axios统一处理" class="headerlink" title="六、使用axios统一处理"></a>六、使用axios统一处理</h1><h2 id="6-1-示例代码"><a href="#6-1-示例代码" class="headerlink" title="6.1 示例代码"></a>6.1 示例代码</h2><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// request.js</span></span><br><span class="line"><span class="keyword">import</span> axios <span class="keyword">from</span> <span class="string">'axios'</span>;</span><br><span class="line"><span class="keyword">import</span> NProgress <span class="keyword">from</span> <span class="string">'nprogress'</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; notification, message &#125; <span class="keyword">from</span> <span class="string">'antd'</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; routerRedux &#125; <span class="keyword">from</span> <span class="string">'dva/router'</span>;</span><br><span class="line"><span class="keyword">import</span> store <span class="keyword">from</span> <span class="string">'../index'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 一、功能：</span></span><br><span class="line"><span class="comment"> * 1. 统一拦截http错误请求码；</span></span><br><span class="line"><span class="comment"> * 2. 统一拦截业务错误代码；</span></span><br><span class="line"><span class="comment"> * 3. 统一设置请求前缀</span></span><br><span class="line"><span class="comment"> * |-- 每个 http 加前缀 baseURL = /api/v1，从配置文件中获取 apiPrefix</span></span><br><span class="line"><span class="comment"> * 4. 配置异步请求过渡状态：显示蓝色加载条表示正在请求中，避免给用户页面假死的不好体验。</span></span><br><span class="line"><span class="comment"> * |-- 使用 NProgress 工具库。</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * 二、引包：</span></span><br><span class="line"><span class="comment"> * |-- axios：http 请求工具库</span></span><br><span class="line"><span class="comment"> * |-- NProgress：异步请求过度条，在浏览器主体部分顶部显示蓝色小条</span></span><br><span class="line"><span class="comment"> * |-- notification：Antd组件 &gt; 处理错误响应码提示信息</span></span><br><span class="line"><span class="comment"> * |-- routerRedux：dva/router对象，用于路由跳转，错误响应码跳转相应页面</span></span><br><span class="line"><span class="comment"> * |-- store：dva中对象，使用里面的 dispatch 对象，用于触发路由跳转</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 设置全局参数，如响应超市时间，请求前缀等。</span></span><br><span class="line">axios.defaults.timeout = <span class="number">5000</span></span><br><span class="line">axios.defaults.baseURL = <span class="string">'/api/v1'</span>;</span><br><span class="line">axios.defaults.withCredentials = <span class="literal">true</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 状态码错误信息</span></span><br><span class="line"><span class="keyword">const</span> codeMessage = &#123;</span><br><span class="line">  <span class="number">200</span>: <span class="string">'服务器成功返回请求的数据。'</span>,</span><br><span class="line">  <span class="number">201</span>: <span class="string">'新建或修改数据成功。'</span>,</span><br><span class="line">  <span class="number">202</span>: <span class="string">'一个请求已经进入后台排队（异步任务）。'</span>,</span><br><span class="line">  <span class="number">204</span>: <span class="string">'删除数据成功。'</span>,</span><br><span class="line">  <span class="number">400</span>: <span class="string">'发出的请求有错误，服务器没有进行新建或修改数据的操作。'</span>,</span><br><span class="line">  <span class="number">401</span>: <span class="string">'用户没有权限（令牌、用户名、密码错误）。'</span>,</span><br><span class="line">  <span class="number">403</span>: <span class="string">'用户得到授权，但是访问是被禁止的。'</span>,</span><br><span class="line">  <span class="number">404</span>: <span class="string">'发出的请求针对的是不存在的记录，服务器没有进行操作。'</span>,</span><br><span class="line">  <span class="number">406</span>: <span class="string">'请求的格式不可得。'</span>,</span><br><span class="line">  <span class="number">410</span>: <span class="string">'请求的资源被永久删除，且不会再得到的。'</span>,</span><br><span class="line">  <span class="number">422</span>: <span class="string">'当创建一个对象时，发生一个验证错误。'</span>,</span><br><span class="line">  <span class="number">500</span>: <span class="string">'服务器发生错误，请检查服务器。'</span>,</span><br><span class="line">  <span class="number">502</span>: <span class="string">'网关错误。'</span>,</span><br><span class="line">  <span class="number">503</span>: <span class="string">'服务不可用，服务器暂时过载或维护。'</span>,</span><br><span class="line">  <span class="number">504</span>: <span class="string">'网关超时。'</span>,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 添加一个请求拦截器，用于设置请求过渡状态</span></span><br><span class="line">axios.interceptors.request.use(<span class="function">(<span class="params">config</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="comment">// 请求开始，蓝色过渡滚动条开始出现</span></span><br><span class="line">  NProgress.start();</span><br><span class="line">  <span class="keyword">return</span> config;</span><br><span class="line">&#125;, (error) =&gt; &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">Promise</span>.reject(error);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 添加一个返回拦截器</span></span><br><span class="line">axios.interceptors.response.use(<span class="function">(<span class="params">response</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="comment">// 请求结束，蓝色过渡滚动条消失</span></span><br><span class="line">  NProgress.done();</span><br><span class="line">  <span class="keyword">return</span> response;</span><br><span class="line">&#125;, (error) =&gt; &#123;</span><br><span class="line">  <span class="comment">// 请求结束，蓝色过渡滚动条消失</span></span><br><span class="line">  <span class="comment">// 即使出现异常，也要调用关闭方法，否则一直处于加载状态很奇怪</span></span><br><span class="line">  NProgress.done();</span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">Promise</span>.reject(error);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="function"><span class="keyword">function</span> <span class="title">request</span> (<span class="params">opt</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 调用 axios api，统一拦截</span></span><br><span class="line">  <span class="keyword">return</span> axios(opt)</span><br><span class="line">    .then(<span class="function">(<span class="params">response</span>) =&gt;</span> </span><br><span class="line">      <span class="comment">// &gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; 请求成功 &lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;</span></span><br><span class="line">      <span class="built_in">console</span>.log(<span class="string">`【<span class="subst">$&#123;opt.method&#125;</span> <span class="subst">$&#123;opt.url&#125;</span>】请求成功，响应数据：%o`</span>, response);</span><br><span class="line"></span><br><span class="line">      <span class="comment">// 打印业务错误提示</span></span><br><span class="line">      <span class="keyword">if</span> (response.data &amp;&amp; response.data.code != <span class="string">'0000'</span>) &#123;</span><br><span class="line">        message.error(response.data.message);</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">return</span> &#123; ...response.data &#125;;</span><br><span class="line">    &#125;)</span><br><span class="line">    .catch(<span class="function">(<span class="params">error</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="comment">// &gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; 请求失败 &lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;</span></span><br><span class="line">      <span class="comment">// 请求配置发生的错误</span></span><br><span class="line">      <span class="keyword">if</span> (!error.response) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">console</span>.log(<span class="string">'Error'</span>, error.message);</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="comment">// 响应时状态码处理 </span></span><br><span class="line">      <span class="keyword">const</span> status = error.response.status;</span><br><span class="line">      <span class="keyword">const</span> errortext = codeMessage[status] || error.response.statusText;</span><br><span class="line">      </span><br><span class="line">      notification.error(&#123;</span><br><span class="line">        message: <span class="string">`请求错误 <span class="subst">$&#123;status&#125;</span>`</span>,</span><br><span class="line">        description: errortext,</span><br><span class="line">      &#125;);</span><br><span class="line">      </span><br><span class="line">      <span class="comment">// 存在请求，但是服务器的返回一个状态码，它们都在2xx之外</span></span><br><span class="line">      <span class="keyword">const</span> &#123; dispatch &#125; = store;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">if</span> (status === <span class="number">401</span>) &#123;</span><br><span class="line">        dispatch(routerRedux.push(<span class="string">'/user/login'</span>));</span><br><span class="line">      &#125; <span class="keyword">else</span> <span class="keyword">if</span> (status === <span class="number">403</span>) &#123;</span><br><span class="line">        dispatch(routerRedux.push(<span class="string">'/exception/403'</span>));</span><br><span class="line">      &#125; <span class="keyword">else</span> <span class="keyword">if</span> (status &lt;= <span class="number">504</span> &amp;&amp; status &gt;= <span class="number">500</span>) &#123;</span><br><span class="line">        dispatch(routerRedux.push(<span class="string">'/exception/500'</span>));</span><br><span class="line">      &#125; <span class="keyword">else</span> <span class="keyword">if</span> (status &gt;= <span class="number">404</span> &amp;&amp; status &lt; <span class="number">422</span>) &#123;</span><br><span class="line">        dispatch(routerRedux.push(<span class="string">'/exception/404'</span>));</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="comment">// 开发时使用，上线时删除</span></span><br><span class="line">      <span class="built_in">console</span>.log(<span class="string">`【<span class="subst">$&#123;opt.method&#125;</span> <span class="subst">$&#123;opt.url&#125;</span>】请求失败，响应数据：%o`</span>, error.response);</span><br><span class="line"></span><br><span class="line">      <span class="keyword">return</span> &#123; <span class="attr">code</span>: status, <span class="attr">message</span>: errortext &#125;; </span><br><span class="line">    &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="6-2-明确响应体"><a href="#6-2-明确响应体" class="headerlink" title="6.2 明确响应体"></a>6.2 明确响应体</h2><blockquote>
<p>以微信小程序为例，请求响应数据分为两部分：</p>
</blockquote>
<ul>
<li>网络请求是否成功；</li>
<li>业务场景值。即便网络请求成功了，业务处理上可能有时也会出错，比如校验不通过</li>
</ul>
<p>我们在拦截响应时要分别对这两部分进行处理</p>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">response = &#123;</span><br><span class="line">  status: <span class="number">200</span>,                <span class="comment">// 网络请求状态。</span></span><br><span class="line">  statusText: <span class="string">'xxx'</span>,</span><br><span class="line">  data: &#123;</span><br><span class="line">    code: <span class="string">'1001'</span>,             <span class="comment">// 业务请求状态。这里 '0000' 表示业务没问题，其它都有问题</span></span><br><span class="line">    message: <span class="string">'yyy'</span>,</span><br><span class="line">    data: &#123;  &#125;,</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="6-3-依赖包分析"><a href="#6-3-依赖包分析" class="headerlink" title="6.3 依赖包分析"></a>6.3 依赖包分析</h2><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> axios <span class="keyword">from</span> <span class="string">'axios'</span>;</span><br><span class="line"><span class="keyword">import</span> NProgress <span class="keyword">from</span> <span class="string">'nprogress'</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; notification, message &#125; <span class="keyword">from</span> <span class="string">'antd'</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; routerRedux &#125; <span class="keyword">from</span> <span class="string">'dva/router'</span>;</span><br><span class="line"><span class="keyword">import</span> store <span class="keyword">from</span> <span class="string">'../index'</span>;</span><br></pre></td></tr></table></figure>
<blockquote>
<p><code>import store from &#39;../index&#39;;</code>这是 <code>dva</code> 中导出的对象。即下面代码最终导出的 <code>app._store</code>，引入它是因为 <code>dispatch</code> 对象在里面，我们需要 dispatch 对象进行路由跳转</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// index.js</span></span><br><span class="line"><span class="keyword">import</span> dva <span class="keyword">from</span> <span class="string">'dva'</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; message &#125; <span class="keyword">from</span> <span class="string">'antd'</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; createBrowserHistory <span class="keyword">as</span> createHistory &#125; <span class="keyword">from</span> <span class="string">'history'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 1. Initialize</span></span><br><span class="line"><span class="keyword">const</span> app = dva(&#123;</span><br><span class="line">  history: createHistory(),</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. Plugins</span></span><br><span class="line">app.use(createLoading());</span><br><span class="line"></span><br><span class="line"><span class="comment">// 3. Model</span></span><br><span class="line">app.model(<span class="built_in">require</span>(<span class="string">'./models/app/global'</span>).default);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 4. Router</span></span><br><span class="line">app.router(<span class="built_in">require</span>(<span class="string">'./router'</span>).default);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 5. Start</span></span><br><span class="line">app.start(<span class="string">'#root'</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> app._store;</span><br></pre></td></tr></table></figure>
<h2 id="6-4-axios-全局配置"><a href="#6-4-axios-全局配置" class="headerlink" title="6.4 axios 全局配置"></a>6.4 axios 全局配置</h2><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 设置全局参数，如响应超市时间，请求前缀等。</span></span><br><span class="line">axios.defaults.timeout = <span class="number">5000</span></span><br><span class="line">axios.defaults.baseURL = <span class="string">'/api/v1'</span>;</span><br><span class="line">axios.defaults.withCredentials = <span class="literal">true</span>;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>axios 可以设置很多全局配置，具体可参阅 <a href="https://segmentfault.com/a/1190000008470355" target="_blank" rel="noopener">https://segmentfault.com/a/1190000008470355</a></p>
</blockquote>
<h2 id="6-5-加载-NProgress-过渡组件"><a href="#6-5-加载-NProgress-过渡组件" class="headerlink" title="6.5 加载 NProgress 过渡组件"></a>6.5 加载 NProgress 过渡组件</h2><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">/ 添加一个请求拦截器，用于设置请求过渡状态</span><br><span class="line">axios.interceptors.request.use(<span class="function">(<span class="params">config</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="comment">// 请求开始，蓝色过渡滚动条开始出现</span></span><br><span class="line">  NProgress.start();</span><br><span class="line">  <span class="keyword">return</span> config;</span><br><span class="line">&#125;, (error) =&gt; &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">Promise</span>.reject(error);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 添加一个返回拦截器</span></span><br><span class="line">axios.interceptors.response.use(<span class="function">(<span class="params">response</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="comment">// 请求结束，蓝色过渡滚动条消失</span></span><br><span class="line">  NProgress.done();</span><br><span class="line">  <span class="keyword">return</span> response;</span><br><span class="line">&#125;, (error) =&gt; &#123;</span><br><span class="line">  <span class="comment">// 请求结束，蓝色过渡滚动条消失</span></span><br><span class="line">  <span class="comment">// 即使出现异常，也要调用关闭方法，否则一直处于加载状态很奇怪</span></span><br><span class="line">  NProgress.done();</span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">Promise</span>.reject(error);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<blockquote>
<p><code>NProgress</code> 的使用主要有两个方法，当调用 <code>NProgress.start();</code> 时在浏览器顶部就会出现蓝色小条，当调用 <code>NProgress.done();</code> 蓝色小条就会消失。我们分别在请求开始和接收到响应调用这两个方法</p>
</blockquote>
<p><img src="https://upload-images.jianshu.io/upload_images/6693922-948c3efcfeeaf4fd.gif" alt></p>
<h2 id="6-6-网络请求成功处理"><a href="#6-6-网络请求成功处理" class="headerlink" title="6.6 网络请求成功处理"></a>6.6 网络请求成功处理</h2><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">.then(<span class="function">(<span class="params">response</span>) =&gt;</span> </span><br><span class="line">      <span class="comment">// &gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; 请求成功 &lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;</span></span><br><span class="line">      <span class="built_in">console</span>.log(<span class="string">`【<span class="subst">$&#123;opt.method&#125;</span> <span class="subst">$&#123;opt.url&#125;</span>】请求成功，响应数据：%o`</span>, response);</span><br><span class="line"></span><br><span class="line">      <span class="comment">// 打印业务错误提示</span></span><br><span class="line">      <span class="keyword">if</span> (response.data &amp;&amp; response.data.code != <span class="string">'0000'</span>) &#123;</span><br><span class="line">        message.error(response.data.message);</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">return</span> &#123; ...response.data &#125;;</span><br><span class="line">    &#125;)</span><br></pre></td></tr></table></figure>
<blockquote>
<p>网络请求状态码为 <code>200-300</code> 表示成功，此时还应该判断业务处理是否成功。这个根据具体项目具体规定，比如微信小程序有一套场景值。在实际项目中可以自行规定 <code>code = &#39;0000&#39;</code> 业务处理完全没问题，<code>code = &#39;1111&#39;</code>校验不通过，<code>code = &#39;2222&#39;</code> 数据库出错等等。</p>
</blockquote>
<ul>
<li>最后别忘了要返回具体对象 <code>{ ...response.data }</code></li>
</ul>
<h2 id="6-7-网络请求失败处理"><a href="#6-7-网络请求失败处理" class="headerlink" title="6.7 网络请求失败处理"></a>6.7 网络请求失败处理</h2><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 状态码错误信息</span></span><br><span class="line"><span class="keyword">const</span> codeMessage = &#123;</span><br><span class="line">  <span class="number">200</span>: <span class="string">'服务器成功返回请求的数据。'</span>,</span><br><span class="line">  <span class="number">201</span>: <span class="string">'新建或修改数据成功。'</span>,</span><br><span class="line">  <span class="number">202</span>: <span class="string">'一个请求已经进入后台排队（异步任务）。'</span>,</span><br><span class="line">  <span class="number">204</span>: <span class="string">'删除数据成功。'</span>,</span><br><span class="line">  <span class="number">400</span>: <span class="string">'发出的请求有错误，服务器没有进行新建或修改数据的操作。'</span>,</span><br><span class="line">  <span class="number">401</span>: <span class="string">'用户没有权限（令牌、用户名、密码错误）。'</span>,</span><br><span class="line">  <span class="number">403</span>: <span class="string">'用户得到授权，但是访问是被禁止的。'</span>,</span><br><span class="line">  <span class="number">404</span>: <span class="string">'发出的请求针对的是不存在的记录，服务器没有进行操作。'</span>,</span><br><span class="line">  <span class="number">406</span>: <span class="string">'请求的格式不可得。'</span>,</span><br><span class="line">  <span class="number">410</span>: <span class="string">'请求的资源被永久删除，且不会再得到的。'</span>,</span><br><span class="line">  <span class="number">422</span>: <span class="string">'当创建一个对象时，发生一个验证错误。'</span>,</span><br><span class="line">  <span class="number">500</span>: <span class="string">'服务器发生错误，请检查服务器。'</span>,</span><br><span class="line">  <span class="number">502</span>: <span class="string">'网关错误。'</span>,</span><br><span class="line">  <span class="number">503</span>: <span class="string">'服务不可用，服务器暂时过载或维护。'</span>,</span><br><span class="line">  <span class="number">504</span>: <span class="string">'网关超时。'</span>,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ...........</span></span><br><span class="line"></span><br><span class="line">.catch(<span class="function">(<span class="params">error</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="comment">// &gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; 请求失败 &lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;</span></span><br><span class="line">      <span class="comment">// 请求配置发生的错误</span></span><br><span class="line">      <span class="keyword">if</span> (!error.response) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">console</span>.log(<span class="string">'Error'</span>, error.message);</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="comment">// 响应时状态码处理 </span></span><br><span class="line">      <span class="keyword">const</span> status = error.response.status;</span><br><span class="line">      <span class="keyword">const</span> errortext = codeMessage[status] || error.response.statusText;</span><br><span class="line">      </span><br><span class="line">      notification.error(&#123;</span><br><span class="line">        message: <span class="string">`请求错误 <span class="subst">$&#123;status&#125;</span>`</span>,</span><br><span class="line">        description: errortext,</span><br><span class="line">      &#125;);</span><br><span class="line">      </span><br><span class="line">      <span class="comment">// 存在请求，但是服务器的返回一个状态码，它们都在2xx之外</span></span><br><span class="line">      <span class="keyword">const</span> &#123; dispatch &#125; = store;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">if</span> (status === <span class="number">401</span>) &#123;</span><br><span class="line">        dispatch(routerRedux.push(<span class="string">'/user/login'</span>));</span><br><span class="line">      &#125; <span class="keyword">else</span> <span class="keyword">if</span> (status === <span class="number">403</span>) &#123;</span><br><span class="line">        dispatch(routerRedux.push(<span class="string">'/exception/403'</span>));</span><br><span class="line">      &#125; <span class="keyword">else</span> <span class="keyword">if</span> (status &lt;= <span class="number">504</span> &amp;&amp; status &gt;= <span class="number">500</span>) &#123;</span><br><span class="line">        dispatch(routerRedux.push(<span class="string">'/exception/500'</span>));</span><br><span class="line">      &#125; <span class="keyword">else</span> <span class="keyword">if</span> (status &gt;= <span class="number">404</span> &amp;&amp; status &lt; <span class="number">422</span>) &#123;</span><br><span class="line">        dispatch(routerRedux.push(<span class="string">'/exception/404'</span>));</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="comment">// 开发时使用，上线时删除</span></span><br><span class="line">      <span class="built_in">console</span>.log(<span class="string">`【<span class="subst">$&#123;opt.method&#125;</span> <span class="subst">$&#123;opt.url&#125;</span>】请求失败，响应数据：%o`</span>, error.response);</span><br><span class="line"></span><br><span class="line">      <span class="keyword">return</span> &#123; <span class="attr">code</span>: status, <span class="attr">message</span>: errortext &#125;; </span><br><span class="line">    &#125;);</span><br></pre></td></tr></table></figure>
<ul>
<li>网络请求失败，首先需要根据 <code>status</code> 打印提示消息，告诉用户为什么请求失败。如响应码为 <code>401</code>，那么提示用户的文字就会是 用户没有权限（令牌、用户名、密码错误）</li>
<li>如果是 <code>401</code> 错误，表示用户没有权限访问或者用户名密码输入错误，应该跳转到登录页面：<code>dispatch(routerRedux.push(&#39;/user/login&#39;));</code></li>
</ul>
<h1 id="七、更多参考"><a href="#七、更多参考" class="headerlink" title="七、更多参考"></a>七、更多参考</h1><ul>
<li><a href="https://github.com/dvajs/dva-docs/blob/master/v1/zh-cn/tutorial/01-%E6%A6%82%E8%A6%81.md" target="_blank" rel="noopener">dva官方教程</a></li>
<li><a href="https://github.com/dvajs/dva/blob/master/README_zh-CN.md" target="_blank" rel="noopener">官方文档</a></li>
<li><a href="https://github.com/dvajs/dva-knowledgemap" target="_blank" rel="noopener">使用Dva的所有知识点</a></li>
<li><a href="http://slides.com/sorrycc/dva#/" target="_blank" rel="noopener">Dva-React 应用框架在蚂蚁金服的实践</a></li>
<li><a href="https://github.com/sorrycc/roadhog#%E9%85%8D%E7%BD%AE" target="_blank" rel="noopener">roadhog介绍</a></li>
<li><a href="https://my.oschina.net/dkvirus/blog/1057996" target="_blank" rel="noopener">创建一个 dva 脚手架工程</a></li>
<li><a href="https://my.oschina.net/dkvirus/blog/1058203" target="_blank" rel="noopener">dva 脚手架目录分析</a></li>
<li><a href="https://github.com/sorrycc/blog/issues/18" target="_blank" rel="noopener">12 步 30 分钟，完成用户管理的 CURD 应用 (react+dva+antd)</a></li>
<li><a href="https://www.jianshu.com/p/c5ec9ffa29be" target="_blank" rel="noopener">dva router4.0 使用实践总结</a></li>
<li><a href="https://www.jianshu.com/p/7de59752b8a8" target="_blank" rel="noopener">dva 2.0中如何使用代码进行路由跳转</a></li>
<li><a href="https://www.jianshu.com/p/2e9e45e9a880" target="_blank" rel="noopener">dva 配置 browserHistory 实践总结</a></li>
<li><a href="https://www.jianshu.com/p/61fe7a57fad4" target="_blank" rel="noopener">dva-loading 实践用法</a></li>
<li><a href="https://www.jianshu.com/p/649e97ff4354" target="_blank" rel="noopener">dva 升级2.0版本遇到的问题小结</a></li>
<li><a href="https://www.jianshu.com/p/e0a220906301" target="_blank" rel="noopener">dva 中进行页面复用实践总结</a></li>
<li><a href="https://dvajs.com/knowledgemap/#javascript-%E8%AF%AD%E8%A8%80" target="_blank" rel="noopener">Dva知识地图</a></li>
<li><a href="https://dvajs.com/api/" target="_blank" rel="noopener">dva-API文档</a></li>
</ul>

      </div>
    
  </div>

</article>

<button class="assist-btn2 circle" id="assist_btn2" title="点亮屏幕" style="left: 27px; top: 152px;">
  <i class="iconfont" style="display:inline-block;color:red;width:20px;height:20px;">&#xe61d;</i>
</button>
<button class="assist-btn1 circle" id="assist_btn1" title="关闭屏幕亮度" style="left: 27px; top: 152px;">
  <i class="iconfont toc-title" style="display:inline-block;color:red;width:20px;height:20px;">&#xe61d;</i>
</button>


<script src="//cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script>	

<script src="https://my.openwrite.cn/js/readmore.js" type="text/javascript"></script>
<script>
  const btw = new BTWPlugin();
  btw.init({
    id: "container",
    blogId: "22699-1592137983091-414",
    name: "前端进阶之旅",
    qrcode: "https://poetries1.gitee.io/img-repo/2020/06/qrcode.jpg",
    keyword: "3a3b3c",
  });
</script>

<script type="text/javascript">

// white theme
var body = {color: "#555", background: "#000"};
var a_tag = {color: "#222"};
var header = { background: "#222"};
var logo_line_i = {background: "#222"};
// var post_code = {background: "#eee", color: "#222"};

function switch_theme() {
 $("body").css(body);
 $("a:not('.links-of-author-item a, .site-state-item a, .site-state-posts a, .feed-link a, .motion-element a, .post-tags a, .show-commit-cls a, #donate_board a')").css(a_tag);
 $(".header, .footer").css(header);
 $(".logo-line-before i, .logo-line-after i").css(logo_line_i);
 //$(".post code").css(post_code);
 $("#idhyt-surprise-ball #idhyt-surprise-ball-animation .drag").css(a_tag);
 $(".post-title-link, .posts-expand .post-meta, .post-comments-count, .disqus-comment-count, .post-category a, .post-nav-next a, .post-nav-item a").css(a_tag);
 
 // $("code").css({color: '#c5c8c6', background: '#1d1f21'});
 //$("#assist_btn1").hide(1500);
}

$(function () {
$("#assist_btn2").css("display","none");
 $("#assist_btn1").click(function() {
     switch_theme();
$("div#toc.toc-article").css({
 "background":"#eaeaea",
 "opacity":1
});
$(".toc-article ol").show();
$("#toc.toc-article .toc-title").css("color","#a98602");
$("#assist_btn1").css("display","none");
$("#assist_btn2").css("display","block");
 });
$("#assist_btn2").click(function() {
$("#assist_btn2").css("display","none");
$("#assist_btn1").css("display","block");
$("body").css("background","url(http://www.miaov.com/static/ie/images/news/bg.png)")
     $(".header, .footer").css("background","url(http://www.miaov.com/static/ie/images/news/bg.png)")
$(".toc-article ol").toggle(1000);
 });
});


//背景随机

var Y, O, E, L, B, C, T, z, N, S, A, I;
!function() {
var e = function() {
for (O.clearRect(0, 0, L, B), T = [{
x: 0,
y: .7 * B + C
}, {
x: 0,
y: .7 * B - C
}]; T[1].x < L + C;) t(T[0], T[1])
}, t = function(e, t) {
O.beginPath(), O.moveTo(e.x, e.y), O.lineTo(t.x, t.y);
var n = t.x + (2 * I() - .25) * C,
 r = a(t.y);
O.lineTo(n, r), O.closePath(), N -= S / -50, O.fillStyle = "#" + (127 * A(N) + 128 << 16 | 127 * A(N + S / 3) + 128 << 8 | 127 * A(N + S / 3 * 2) + 128).toString(16), O.fill(), T[0] = T[1], T[1] = {
 x: n,
 y: r
}
}, a = function n(e) {
var t = e + (2 * I() - 1.1) * C;
return t > B || t < 0 ? n(e) : t
};
Y = document.getElementById("evanyou"), O = Y.getContext("2d"), E = window.devicePixelRatio || 1, L = window.innerWidth, B = window.innerHeight, C = 90, z = Math, N = 0, S = 2 * z.PI, A = z.cos, I = z.random, Y.width = L * E, Y.height = B * E, O.scale(E, E), O.globalAlpha = .6, document.onclick = e, document.ontouchstart = e, e()
}()

   
$("#toc-eye").click(function(){
$("#toc.toc-article").toggle(1000);
});

</script>


   
  <div class="text-center donation">
    <div class="inner-donation">
      <span class="btn-donation">支持一下</span>
      <div class="donation-body">
        <div class="tip text-center">扫一扫，支持poetries</div>
        <ul>
        
          <li class="item">
            
              <span>微信扫一扫</span>
            
            <img src="/images/weixin.jpg" alt="">
          </li>
        
          <li class="item">
            
              <span>支付宝扫一扫</span>
            
            <img src="/images/zhifubao.jpg" alt="">
          </li>
        
        </ul>
      </div>
    </div>
  </div>


   
  <div class="box-prev-next clearfix">
    <a class="show pull-left" href="/2018/09/04/pwa/">
        <i class="icon icon-angle-left"></i>
    </a>
    <a class="show pull-right" href="/2018/09/07/umi-dva/">
        <i class="icon icon-angle-right"></i>
    </a>
  </div>




</div>


  <a id="backTop" class="back-top">
    <i class="icon-angle-up"></i>
  </a>




  <div class="modal" id="modal">
  <span id="cover" class="cover hide"></span>
  <div id="modal-dialog" class="modal-dialog hide-dialog">
    <div class="modal-header">
      <span id="close" class="btn-close">关闭</span>
    </div>
    <hr>
    <div class="modal-body">
      <ul class="list-toolbox">
        
          <li class="item-toolbox">
            <a
              class="CIRCLE"
              href="/archives/"
              rel="noopener noreferrer"
              target="_self"
              >
              博客
            </a>
          </li>
        
          <li class="item-toolbox">
            <a
              class="CIRCLE"
              href="/categories/"
              rel="noopener noreferrer"
              target="_self"
              >
              分类
            </a>
          </li>
        
          <li class="item-toolbox">
            <a
              class="CIRCLE"
              href="/tags/"
              rel="noopener noreferrer"
              target="_self"
              >
              标签
            </a>
          </li>
        
          <li class="item-toolbox">
            <a
              class="CIRCLE"
              href="/search/"
              rel="noopener noreferrer"
              target="_self"
              >
              搜索
            </a>
          </li>
        
          <li class="item-toolbox">
            <a
              class="CIRCLE"
              href="/link/"
              rel="noopener noreferrer"
              target="_self"
              >
              友链
            </a>
          </li>
        
          <li class="item-toolbox">
            <a
              class="CIRCLE"
              href="/about/"
              rel="noopener noreferrer"
              target="_self"
              >
              关于
            </a>
          </li>
        
      </ul>

    </div>
  </div>
</div>



  
      <div class="fexo-comments comments-post">
    

    

    
    

    

    
    

    

<!-- Gitalk评论插件通用代码 -->
<div id="gitalk-container"></div>

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.css">
<script src="https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.min.js"></script>
<script>
const gitalk = new Gitalk({
  clientID: '5567a2c4abb858009d96',
  clientSecret: 'b9039ec056cf5c2346b3cdb63308a28c163f91e5',
  repo: 'poetries.github.io',
  owner: 'poetries',
  // 在这里设置一下截取前50个字符串, 这是因为 github 对 label 的长度有了要求, 如果超过
  // 50个字符串则会报错.
  // id: location.pathname.split('/').pop().substring(0, 49),
  id: location.pathname,
  admin: ['poetries'],
  // facebook-like distraction free mode
  distractionFreeMode: false
})
gitalk.render('gitalk-container')
</script>
<!-- Gitalk代码结束 -->



  </div>

  

  <script type="text/javascript">
  function loadScript(url, callback) {
    var script = document.createElement('script')
    script.type = 'text/javascript';

    if (script.readyState) { //IE
      script.onreadystatechange = function() {
        if (script.readyState == 'loaded' ||
          script.readyState == 'complete') {
          script.onreadystatechange = null;
          callback();
        }
      };
    } else { //Others
      script.onload = function() {
        callback();
      };
    }

    script.src = url;
    document.getElementsByTagName('head')[0].appendChild(script);
  }

  window.onload = function() {
    loadScript('/js/bundle.js?235683', function() {
      // load success
    });
  }
</script>


  <!-- 页面点击小红心 -->
  <script type="text/javascript" src="/js/clicklove.js"></script>
 
  
</body>
</html>
